mass storage drivers and drive letters?

Hi,

I have looked at the usbview application in the DDK and know how to get the mass storage driver key name. However, I don’t know how to get the drive handles that are exposed by the windows drivers for this particular device.

For example.

* I can get the driver key name for the mass storages driver in question after enumerating the PCI/USB tree for a particular card reader I am interested in. This works fine. This card reader exposes four luns or four drives.
However, how do I figure out the drive letters that are supported by this particular device?

PCs in question can have many usb card readers.

If anyone has any suggestions, I would appreciate your help.

Regards,
Yogesh

One common way is to open each device by its device interface and issue an IOCTL_STORAGE_GET_DEVICE_NUMBER. Then open each drive letter and issue the same IOCTL. When you have drive numbers that match you’ve found your device.

-p

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@austin.rr.com
Sent: Monday, August 20, 2007 7:21 AM
To: Windows System Software Devs Interest List
Subject: [ntdev] mass storage drivers and drive letters?

Hi,

I have looked at the usbview application in the DDK and know how to get the mass storage driver key name. However, I don’t know how to get the drive handles that are exposed by the windows drivers for this particular device.

For example.

* I can get the driver key name for the mass storages driver in question after enumerating the PCI/USB tree for a particular card reader I am interested in. This works fine. This card reader exposes four luns or four drives.
However, how do I figure out the drive letters that are supported by this particular device?

PCs in question can have many usb card readers.

If anyone has any suggestions, I would appreciate your help.

Regards,
Yogesh


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

Peter Wieland wrote:

One common way is to open each device by its device interface and issue an IOCTL_STORAGE_GET_DEVICE_NUMBER. Then open each drive letter and issue the same IOCTL. When you have drive numbers that match you’ve found your device.

You know, this has turned into a Frequently Asked Question. I myself
have had two clients that needed the ability to find a drive letter
given a USB VID/PID. Finding no better solution at the time, I used
registry diving to solve the problem.

Perhaps it would be a good time for someone to write a knowledge base or
blog article suggesting a relatively “blessed” method for performing
this mapping.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

> One common way is to open each device by its device interface and issue an

IOCTL_STORAGE_GET_DEVICE_NUMBER. Then open each drive letter and issue the same
IOCTL. When you have drive numbers that match you’ve found your device.

You make an assumption that the target disk is removable, i.e. that it may have only one physical partition and that drive letter corresponds to the physical partition itself ( Ftdisk.sys does not get involved with removable media, so that logical volumes don’t apply). If this is the case, then, indeed, the above approach will work just fine.

However, please don’t forget that USB device may present itself as a basic, rather than removable, disk. In such case we have 2 problems with the above approach:

  1. Logical volumes may be mounted on the target device, so that drive letter will correspond not to the physical partition itself but to the logical volume that is mounted on it. IOCTL_STORAGE_GET_DEVICE_NUMBER is a disk-related IOCTL that has to be sent to the storage stack where Disk.sys participates, so that sending it to Ftdisk.sys is going to be, apparently, of no help here

  2. To make things even more complex, the target device may have more than one physical partition, and, hence, multiple logical volumes may be mounted on it. Therefore, there is no one-to-one correspondence between a disk number and a drive letter, because multiple drive letters may correspond to the same device.

Anton Bassov

I will do this once. Here is some code I wrote a few years ago for my own
use. It will be somewhat mangled by the email software, but it works and
has worked for several years. It is a mapping of drive letters to physical
drives from a user mode application. You may study it and use it to create
your own implementation as you see fit for any purpose. You are not going
to like it with all the funny C++ features and use of interesting stuff. I
removed the full file names from this post.

// Wmi.h - Header file for Wmi.cpp
//
// Copyright (c) 2003 by David J. Craig
//
class ID1
{
public:
CString Physical;
CString Partition;
CString FullPart;

ID1() : Physical(“”), Partition(“”), FullPart(“”) {}
ID1(CString newPhysical, CString newPartition, CString newFull) : \
Physical(newPhysical), Partition(newPartition), FullPart(newFull) {}
};

class ID2
{
public:
CString Partition;
CString Logical;
CString FullPart;

ID2() : Partition(“”), Logical(“”), FullPart(“”) {}
ID2(CString newPartition, CString newLogical, CString newFull) : \
Partition(newPartition), Logical(newLogical), FullPart(newFull) {}
};

typedef vector < ID1 > VECPHY;
typedef vector < ID2 > VECLOG;

void WINAPI WaitThread(HANDLE hWait);


// Wmi.cpp - source file for WMI interface
//
// Copyright (c) 2003 by David J. Craig
//
// The functions in this file are defined in CDlg.h except for
// WaitThread() which is not a member of the CDlg class.
//
//#pragma warning(disable: 4201)

#include “stdafx.h”

using namespace std;

#include <devioctl.h>
#include <wbemidl.h>
#include
#include
#include <mountmgr.h>
#include <mountdev.h>
#include <strsafe.h>
#include <ntdddisk.h>
#include <ntddscsi.h>
#include <ntddstor.h>
#include “Format32.h”
#include “Format32Wmi.h”
#include “Fat.h”
#include “Lfn.h”
#include “Fatnn.h”
#include “Fat32.h”
#include “Fat16.h”
#include “Fat12.h”
#include “PhysicalDrive.h”
#include “Format32Dlg.h”

#pragma comment(user, “Copyright (c) 2003 by David J. Craig\n”)
#pragma comment(user, “Compiled on " DATE” at " TIME )
#pragma comment(user, “Compiling " FILE )
#pragma comment(user, “Last modified on " TIMESTAMP )

#pragma comment(lib, “wbemuuid.lib”) // WMI library

#pragma message(“Copyright (c) 2003 by David J. Craig\n”)
#pragma message(“Compiled on " DATE” at " TIME )
#pragma message(“Compiling " FILE )
#pragma message(“Last modified on " TIMESTAMP )

int
CFormat32Dlg::GetPhysicalMap(IWbemServices * pIWbemServices)
{
HRESULT hr;
BSTR PropName = NULL;
VARIANT vVal;
ULONG uReturned;
BSTR PerfClass;
CString str;
CString physical;
CString partition;
CString FullPart;
IEnumWbemClassObject * pEnum = NULL;
IWbemClassObject * pPerfInst = NULL;

// Initialization
//
VariantInit(&vVal);
PerfClass = NULL;

// Alloc class name string for DiskPerf
//
PerfClass = SysAllocString(L"Win32_DiskDriveToDiskPartition”);

// Now that the data class info is displayed,
// go get the values for all the disk instances
//
if (!PerfClass)
{
str.Format(_T(“SysAllocString() failed: %u\n”), GetLastError());
AfxMessageBox(str);
return(1);
}

// Create enumerator
//
hr = pIWbemServices->CreateInstanceEnum(
PerfClass,
WBEM_FLAG_SHALLOW,
NULL,
&pEnum
);
SysFreeString(PerfClass);
PerfClass = NULL;

if (hr == WBEM_NO_ERROR)
{
while (pEnum->Next(
INFINITE,
1,
&pPerfInst,
&uReturned) == WBEM_NO_ERROR)
{
// Explicitly get the properties of InstanceName and Active state

// Get the Instance Name string
//
PropName = L"Antecedent”;
if ((pPerfInst->Get(
PropName,
0L,
&vVal,
NULL,
NULL)) == WBEM_NO_ERROR)
{
int i;

ASSERT(V_VT(&vVal) == VT_BSTR);

physical = V_BSTR(&vVal);
i = physical.Find(_T(“PHYSICALDRIVE”));
physical = physical.Right(physical.GetLength() - i);
i = physical.Find(_T(”"”));
physical = physical.Left(i);
VariantClear(&vVal);
}

PropName = L"Dependent";
if ((pPerfInst->Get(
PropName,
0L,
&vVal,
NULL,
NULL)) == WBEM_NO_ERROR)
{
int i;

ASSERT(V_VT(&vVal) == VT_BSTR);

partition = V_BSTR(&vVal);
FullPart = V_BSTR(&vVal);
i = partition.Find(_T(“Disk #”));
partition = partition.Right(partition.GetLength() - i);
i = FullPart.Find(_T(“Disk #”));
FullPart = FullPart.Right(FullPart.GetLength() - i);
i = partition.Find(_T(“, Partition”));
partition = partition.Left(i);
i = FullPart.Find(_T(“"”));
FullPart = FullPart.Left(i);
VariantClear(&vVal);
}
m_Phy.push_back(ID1(physical, partition, FullPart));
pPerfInst->Release();
}
pEnum->Release();
}
else
{
str.Format(_T(“CreateInstanceEnum() failed: %u\n”), GetLastError());
AfxMessageBox(str);
return(2);
}
return(0);
}

int
CFormat32Dlg::GetLogicalMap(IWbemServices * pIWbemServices)
{
HRESULT hr;
BSTR PropName = NULL;
VARIANT vVal;
ULONG uReturned;
BSTR PerfClass;
CString str;
CString logical;
CString partition;
CString FullPart;
IEnumWbemClassObject * pEnum = NULL;
IWbemClassObject * pPerfInst = NULL;

// Initialization
//
VariantInit(&vVal);
PerfClass = NULL;

// Alloc class name string for DiskPerf
//
PerfClass = SysAllocString(L"Win32_LogicalDiskToPartition");

// Now that the data class info is displayed,
// go get the values for all the disk instances
//
if (!PerfClass)
{
str.Format(_T(“SysAllocString() failed: %u\n”), GetLastError());
AfxMessageBox(str);
return(11);
}

// Create enumerator
//
hr = pIWbemServices->CreateInstanceEnum(
PerfClass,
WBEM_FLAG_SHALLOW,
NULL,
&pEnum
);
SysFreeString(PerfClass);
PerfClass = NULL;

if (hr == WBEM_NO_ERROR)
{
while (pEnum->Next(
INFINITE,
1,
&pPerfInst,
&uReturned) == WBEM_NO_ERROR)
{
// Explicitly get the properties of InstanceName and Active state

// Get the Instance Name string
//
PropName = L"Antecedent";
if ((pPerfInst->Get(
PropName,
0L,
&vVal,
NULL,
NULL)) == WBEM_NO_ERROR)
{
int i;

ASSERT(V_VT(&vVal) == VT_BSTR);

partition = V_BSTR(&vVal);
FullPart = V_BSTR(&vVal);
i = partition.Find(_T(“Disk #”));
partition = partition.Right(partition.GetLength() - i);
i = FullPart.Find(_T(“Disk #”));
FullPart = FullPart.Right(FullPart.GetLength() - i);
i = partition.Find(_T(“, Partition”));
partition = partition.Left(i);
i = FullPart.Find(_T(“"”));
FullPart = FullPart.Left(i);
VariantClear(&vVal);
}

PropName = L"Dependent";
if ((pPerfInst->Get(
PropName,
0L,
&vVal,
NULL,
NULL)) == WBEM_NO_ERROR)
{
int i;

ASSERT(V_VT(&vVal) == VT_BSTR);

logical = V_BSTR(&vVal);
i = logical.Find(_T(“"”));
logical = logical.Right(logical.GetLength() - i - 1);
i = logical.Find(_T(“"”));
logical = logical.Left(i);
VariantClear(&vVal);
}
m_Log.push_back(ID2(partition, logical, FullPart));
pPerfInst->Release();
}
pEnum->Release();
}
else
{
str.Format(_T(“CreateInstanceEnum() failed: %u\n”), GetLastError());
AfxMessageBox(str);
return(12);
}
return(0);
}

void WINAPI
WaitThread(HANDLE hWait)
{
while (::WaitForSingleObject(hWait, 300) == WAIT_TIMEOUT)
{
// We don’t want to do anything here.
}
return;
}

BOOL
CFormat32Dlg::InitSecurity(void)
{
// Adjust the security to allow client impersonation.
//
HRESULT hres = CoInitializeSecurity(
NULL,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_SECURE_REFS,
NULL
);
return(SUCCEEDED(hres));
}

int
CFormat32Dlg::GetMapping()
{
HRESULT hr;
DWORD ThreadId;
DWORD dwLen = MAX_COMPUTERNAME_LENGTH + 1;
HANDLE hWait;
HANDLE hThread;
BSTR pNamespace;
TCHAR szSysName[MAX_COMPUTERNAME_LENGTH + 3];
CString str;
IWbemLocator * pIWbemLocator;
IWbemServices * pIWbemServices;

// Initialization
//
pIWbemLocator = NULL;
pIWbemServices = NULL;

if (!SUCCEEDED(CoInitialize(NULL)) || !InitSecurity())
{
str.Format(_T(“COM stuff hosed\n”));
AfxMessageBox(str);
return(21);
}

// Create an instance of the WbemLocator interface.
//
if (CoCreateInstance(
CLSID_WbemLocator,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator,
( LPVOID * ) &pIWbemLocator) == S_OK)
{
StringCbCopy(szSysName, sizeof(szSysName), _T(“\\”));

BOOL bRes = GetComputerName(szSysName + 2, &dwLen);

if (!bRes)
{
str.Format(_T(“GetComputerName() failed: %u\n”), GetLastError());
AfxMessageBox(str);
CoUninitialize();
return(22);
}
pNamespace = SysAllocString(L"root\cimv2");
if (!pNamespace)
{
str.Format(_T(“SysAllocString() failed: %u\n”), GetLastError());
AfxMessageBox(str);
CoUninitialize();
return(23);
}

// make a wait event and kick off wait thread
//
hWait = CreateEvent(NULL, TRUE, FALSE, NULL);
hThread = CreateThread(
NULL,
0,
( LPTHREAD_START_ROUTINE ) WaitThread,
hWait,
0,
&ThreadId
);

// connect and get the IWbemServices pointer
//
hr = pIWbemLocator->ConnectServer(
pNamespace,
NULL,
NULL,
0L,
0L,
NULL,
NULL,
&pIWbemServices
);

// kill wait feedback thread
//
SetEvent(hWait);

// make sure thread is gone
//
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hWait);
SysFreeString(pNamespace);

// See what WBEM/WMI says about
//
if (hr == WBEM_NO_ERROR)
{
// done with locator object
//
pIWbemLocator->Release();

// go enumerate diskperf instances…
//
GetPhysicalMap(pIWbemServices);
GetLogicalMap(pIWbemServices);
pIWbemServices->Release();
}
else
{
str.Format(_T(“Failed to connect with %s: 0x%X\n”),
szSysName,
hr
);
AfxMessageBox(str);
return(24);
}
}
else
{
str.Format(_T(“WMI services not present or unavailable!\n”));
AfxMessageBox(str);
return(25);
}
CoUninitialize();
return(0);
}
__________________________________________________


David J. Craig
Engineer, Sr. Staff Software Systems
Broadcom Corporation

wrote in message news:xxxxx@ntdev…
>> One common way is to open each device by its device interface and issue
>> an
>> IOCTL_STORAGE_GET_DEVICE_NUMBER. Then open each drive letter and issue
>> the same
>> IOCTL. When you have drive numbers that match you’ve found your device.
>
> You make an assumption that the target disk is removable, i.e. that it may
> have only one physical partition and that drive letter corresponds to the
> physical partition itself ( Ftdisk.sys does not get involved with
> removable media, so that logical volumes don’t apply). If this is the
> case, then, indeed, the above approach will work just fine.
>
> However, please don’t forget that USB device may present itself as a
> basic, rather than removable, disk. In such case we have 2 problems with
> the above approach:
>
> 1. Logical volumes may be mounted on the target device, so that drive
> letter will correspond not to the physical partition itself but to the
> logical volume that is mounted on it. IOCTL_STORAGE_GET_DEVICE_NUMBER is a
> disk-related IOCTL that has to be sent to the storage stack where Disk.sys
> participates, so that sending it to Ftdisk.sys is going to be, apparently,
> of no help here
>
> 2. To make things even more complex, the target device may have more than
> one physical partition, and, hence, multiple logical volumes may be
> mounted on it. Therefore, there is no one-to-one correspondence between a
> disk number and a drive letter, because multiple drive letters may
> correspond to the same device.
>
> Anton Bassov
>
></ntddstor.h></ntddscsi.h></ntdddisk.h></strsafe.h></mountdev.h></mountmgr.h></wbemidl.h></devioctl.h>

Use IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS on each volume to get the corresponding disk number(s) for the physical disks on which the volume resides. Between this data and the data from IOCTL_STORAGE_GET_DEVICE_NUMBER you can locate the volumes (and if they have them the drive letters) mounted on any disk device.

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@hotmail.com
Sent: Monday, August 20, 2007 10:31 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] mass storage drivers and drive letters?

One common way is to open each device by its device interface and issue an
IOCTL_STORAGE_GET_DEVICE_NUMBER. Then open each drive letter and issue the same
IOCTL. When you have drive numbers that match you’ve found your device.

You make an assumption that the target disk is removable, i.e. that it may have only one physical partition and that drive letter corresponds to the physical partition itself ( Ftdisk.sys does not get involved with removable media, so that logical volumes don’t apply). If this is the case, then, indeed, the above approach will work just fine.

However, please don’t forget that USB device may present itself as a basic, rather than removable, disk. In such case we have 2 problems with the above approach:

  1. Logical volumes may be mounted on the target device, so that drive letter will correspond not to the physical partition itself but to the logical volume that is mounted on it. IOCTL_STORAGE_GET_DEVICE_NUMBER is a disk-related IOCTL that has to be sent to the storage stack where Disk.sys participates, so that sending it to Ftdisk.sys is going to be, apparently, of no help here

  2. To make things even more complex, the target device may have more than one physical partition, and, hence, multiple logical volumes may be mounted on it. Therefore, there is no one-to-one correspondence between a disk number and a drive letter, because multiple drive letters may correspond to the same device.

Anton Bassov


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

David J. Craig wrote:

I will do this once. Here is some code I wrote a few years ago for my own
use. It will be somewhat mangled by the email software, but it works and
has worked for several years. It is a mapping of drive letters to physical
drives from a user mode application. You may study it and use it to create
your own implementation as you see fit for any purpose. You are not going
to like it with all the funny C++ features and use of interesting stuff. I
removed the full file names from this post.

My own registry-diving version uses an STL vector to store the results,
so there’s nothing here I can’t handle.

Indeed, I got this running, but I don’t think this solves the problem at
hand. This allows me to map physical drive and partition numbers to
drive letters, but this still doesn’t know anything about PnP IDs.
That’s the only way to get USB VID and PID into the picture. Or did I
miss the point?

What’s the point of the CoInitializeSecurity call? Is that just to turn
on local reference counting?


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

It has been so long that I forget why I added that call. It may have been
in some sample I was reading and not needed. With that information you can
obtain the VID/PID by using the SetupDi calls and remember that one of the
items in that vector list is also a key you will see with SetupDi. Also
since you now know the drive letters, you could just call the IOCTL_STORAGE
IoCtl that returns the bus. It depends on if you need a specific VID/PID or
just all volumes on a particular bus or even grouped by bus.

Since I do very little work in the application area, it would take me a
while to fully understand that code again. I tried some of those methods
just because I hadn’t used them before and wanted to see if I could get it
working. This was a personal format utility that handled SmartMedia and
FAT32 volumes larger than 32GB. Recently (this year) I finally began to use
NTFS when Microsoft released the WAIK. I just did not trust a filesystem
that was undocumented and having see some code from the Windows 95 OS at one
time, I was wary of depending upon it. With FAT, there is a good doc that
can be used to repair damaged volumes using WinHex or DiskEdit.

Since I used to work for SmartDisk, I knew the particular formatting that
was used for SmartMedia to keep logical and physical blocks grouped to
reduce wear on the media. I still have a 1998 FujiFilm camera that can only
use 16MB and smaller SmartMedia and I needed a way to reformat them.
SmartMedia and xD Picture Cards do not have embedded controllers, so all
blocking and deblocking has to be done in the software or for mass storage
compliant readers the firmware. Also a LRU algorithm must be used to keep
wear level distributed properly.


David J. Craig
Engineer, Sr. Staff Software Systems
Broadcom Corporation

“Tim Roberts” wrote in message news:xxxxx@ntdev…
> David J. Craig wrote:
>> I will do this once. Here is some code I wrote a few years ago for my
>> own
>> use. It will be somewhat mangled by the email software, but it works and
>> has worked for several years. It is a mapping of drive letters to
>> physical
>> drives from a user mode application. You may study it and use it to
>> create
>> your own implementation as you see fit for any purpose. You are not
>> going
>> to like it with all the funny C++ features and use of interesting stuff.
>> I
>> removed the full file names from this post.
>>
>
> My own registry-diving version uses an STL vector to store the results,
> so there’s nothing here I can’t handle.
>
> Indeed, I got this running, but I don’t think this solves the problem at
> hand. This allows me to map physical drive and partition numbers to
> drive letters, but this still doesn’t know anything about PnP IDs.
> That’s the only way to get USB VID and PID into the picture. Or did I
> miss the point?
>
> What’s the point of the CoInitializeSecurity call? Is that just to turn
> on local reference counting?
>
> –
> Tim Roberts, xxxxx@probo.com
> Providenza & Boekelheide, Inc.
>
>