windows-nt/Source/XPSP1/NT/drivers/storage/mpath/control/pnp.c

910 lines
24 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
#include "mpio.h"
#include <stdio.h>
#include <stdlib.h>
NTSTATUS
MPIOQueryDeviceText(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine handles the QUERY_DEVICE_TEXT Irp for
Pseudo disks.
Arguments:
DeviceObject
Irp
Return Value:
NTSTATUS
--*/
{
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
NTSTATUS status = STATUS_NOT_SUPPORTED;
UCHAR ansiBuffer[256];
ANSI_STRING ansiString;
UNICODE_STRING unicodeString;
PUCHAR index;
PUCHAR inquiryField;
//
// Get the Storage Descriptor from the type extension.
//
deviceDescriptor = diskExtension->DeviceDescriptor;
//
// Set the inquiry data pointer to the front of the descriptor.
//
inquiryField = (PUCHAR)deviceDescriptor;
//
// Zero the string array.
//
RtlZeroMemory(ansiBuffer, sizeof(ansiBuffer));
switch (irpStack->Parameters.QueryDeviceText.DeviceTextType) {
case DeviceTextDescription:
//
// Build <Product><Vendor><"Multi-Path Disk Device">.
//
// Push the inquiry pointer to the VendorId.
//
(ULONG_PTR)inquiryField += deviceDescriptor->VendorIdOffset;
//
// Copy the VendorId to the temp. buffer, and adjust indices.
//
index = ansiBuffer;
RtlCopyMemory(index, inquiryField, 8);
index += 7;
//
// go back and eat all the spaces except for one.
//
while (*index == ' ') {
*index = '\0';
index--;
}
index++;
*index = ' ';
index++;
//
// Handle the ProductID.
//
inquiryField = (PUCHAR)deviceDescriptor;
(ULONG_PTR)inquiryField += deviceDescriptor->ProductIdOffset;
RtlCopyMemory(index, inquiryField, 16);
index += 15;
//
// go back and eat all the spaces except for one.
//
while (*index == ' ') {
*index = '\0';
index--;
}
index++;
*index = ' ';
index++;
//
// Don't use rev or serial number (at least for now)
// TODO: remove or reenable.
//
//inquiryField = (PUCHAR)deviceDescriptor;
//(ULONG_PTR)inquiryField += deviceDescriptor->ProductRevisionOffset;
//RtlCopyMemory(index, inquiryField, 4);
//index += 4;
//
// Append this to the end to distinquish this from the regular scsi drive.
//
sprintf(index, "%s", " Multi-Path Disk Device");
//
// The real wchar buffer is built below.
//
status = STATUS_SUCCESS;
break;
case DeviceTextLocationInformation: {
PSCSI_ADDRESS scsiAddress = NULL;
ULONG i;
PUCHAR index;
UCHAR groupString[25];
//
// Build the group string. Each value in it is the port number of the
// device backing the pseudo disk.
// Adapters(X,Y)
//
RtlZeroMemory(groupString, sizeof(groupString));
sprintf(groupString, "%s", "Port(");
index = groupString;
index += strlen(groupString);
for (i = 0; i < diskExtension->TargetInfoCount; i++) {
//
// Get the SCSI Address for this scsiport PDO
//
status = MPIOGetScsiAddress(diskExtension->TargetInfo[i].PortPdo,
&scsiAddress);
diskExtension->TargetInfo[i].ScsiAddress.PortNumber = scsiAddress->PortNumber;
diskExtension->TargetInfo[i].ScsiAddress.PathId = scsiAddress->PathId;
diskExtension->TargetInfo[i].ScsiAddress.TargetId = scsiAddress->TargetId;
diskExtension->TargetInfo[i].ScsiAddress.Lun = scsiAddress->Lun;
if (status == STATUS_SUCCESS) {
//
// Jam in the PortNumber for this device.
//
sprintf(index, "%d", scsiAddress->PortNumber);
index += strlen(index);
if ((i + 1) == diskExtension->TargetInfoCount) {
//
// Last one, finish off with the closing bracket.
//
sprintf(index,"%s", ")");
} else {
//
// Stick a comma in between each of the port
// numbers.
//
sprintf(index,"%s", ",");
index += strlen(index);
//
// Free the buffer that GetScsiAddress allocated.
//
ExFreePool(scsiAddress);
scsiAddress = NULL;
}
}
}
//
// Free the last one.
//
if (scsiAddress) {
// NOTE: This was above the 'if'. TODO validate and remove this comment.
//
// The last scsiAddress buffer was not freed yet.
// Add in Bus Target Lun based on this Scsi Address.
//
sprintf(ansiBuffer, "%s Bus %d, Target ID %d, LUN %d",
groupString,
scsiAddress->PathId,
scsiAddress->TargetId,
scsiAddress->Lun);
ExFreePool(scsiAddress);
}
break;
}
default:
status = STATUS_NOT_SUPPORTED;
Irp->IoStatus.Information = (ULONG_PTR)NULL;
break;
}
if (status == STATUS_SUCCESS) {
//
// Finally, build the unicode string based on whatever text
// was built above.
//
RtlInitAnsiString(&ansiString, ansiBuffer);
status = RtlAnsiStringToUnicodeString(&unicodeString,
&ansiString,
TRUE);
if (status == STATUS_SUCCESS) {
Irp->IoStatus.Information = (ULONG_PTR)unicodeString.Buffer;
}
}
return status;
}
NTSTATUS
MPIOPdoQdr(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine handles QueryDeviceRelations requests sent to the PDO (PseudoDisks).
Arguments:
DeviceObject
Irp
Return Value:
NTSTATUS
--*/
{
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_RELATIONS deviceRelations;
NTSTATUS status = Irp->IoStatus.Status;
if (irpStack->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation) {
//
// Allocate the return buffer.
//
deviceRelations = ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS));
if (deviceRelations == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
} else {
status = STATUS_SUCCESS;
//
// Indicate, of course, there is One, and it's us.
//
deviceRelations->Count = 1;
deviceRelations->Objects[0] = DeviceObject;
//
// Reference our devObj so that it's not removed.
// PnP will deref it when it's finished with the request.
//
ObReferenceObject(DeviceObject);
Irp->IoStatus.Information = (ULONG_PTR)deviceRelations;
}
}
Irp->IoStatus.Status = status;
return status;
}
NTSTATUS
MPIOHardwareIDs(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
OUT PUNICODE_STRING UnicodeString
)
/*++
Routine Description:
This routine handles QueryHardwareId requests sent to the PDO (PseudoDisks).
Arguments:
DeviceObject
Irp
Return Value:
NTSTATUS
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
NTSTATUS status;
PUCHAR inquiryField;
PUCHAR index;
ULONG bufferLength;
ULONG i;
ANSI_STRING ansiString;
UNICODE_STRING unicodeEntry;
PSTR hwStrings[7];
UCHAR hwId[64];
UCHAR inquiryData[32];
UCHAR savedInquiryData[32];
deviceDescriptor = diskExtension->DeviceDescriptor;
inquiryField = (PUCHAR)deviceDescriptor;
//
// Zero the string array.
//
RtlZeroMemory(hwStrings, sizeof(hwStrings));
RtlZeroMemory(inquiryData, sizeof(inquiryData));
//
// Build the full inquiry data for the device.
// Go through each of the fields in raw data and get
// their offsets. Copy the fields into the buffer.
//
(ULONG_PTR)inquiryField += deviceDescriptor->VendorIdOffset;
index = inquiryData;
RtlCopyMemory(index, inquiryField, 8);
index += 8;
inquiryField = (PUCHAR)deviceDescriptor;
(ULONG_PTR)inquiryField += deviceDescriptor->ProductIdOffset;
RtlCopyMemory(index, inquiryField, 16);
index += 16;
inquiryField = (PUCHAR)deviceDescriptor;
(ULONG_PTR)inquiryField += deviceDescriptor->ProductRevisionOffset;
RtlCopyMemory(index, inquiryField, 4);
//
// Run through and fixup spaces.
//
index = inquiryData;
while (*index != '\0') {
if (*index == ' ') {
*index = '_';
}
index++;
}
//
// Copy this to the saved buffer.
// This is used below while building up each of the HW ID strings.
//
RtlCopyMemory(savedInquiryData, inquiryData, 32);
for (i = 0; i < 6; i++) {
//
// Zero the buffer
//
RtlZeroMemory(hwId, sizeof(hwId));
//
// Each time through the loop, build one of the hardware id strings.
//
// (0) SCSI\DISK\Full Inquiry
// (1) SCSI\Disk\Inquiry - Rev
// (2) SCSI\Disk\Inquiry - Product and rev.
// (3) SCSI\Inquiry with only one char of rev.
// (4) Inquiry with only one char of rev.
// (5) GenDisk
//
switch (i) {
case 0:
//
// Build the header (SCSI\Disk) plus the full inquiry data
//
sprintf(hwId, "%s", "MPIO\\Disk");
index = hwId;
index += strlen(hwId);
RtlMoveMemory(index, inquiryData, strlen(inquiryData));
break;
case 1:
//
// Get rid of the product revision in the inquiryData.
//
index = inquiryData + 24;
*index = '\0';
sprintf(hwId, "%s", "MPIO\\Disk");
index = hwId;
index += strlen(hwId);
RtlMoveMemory(index, inquiryData, strlen(inquiryData));
break;
case 2:
//
// Get rid of the product id.
//
index = inquiryData + 8;
*index = '\0';
sprintf(hwId, "%s", "MPIO\\Disk");
index = hwId;
index += strlen(hwId);
RtlMoveMemory(index, inquiryData, strlen(inquiryData));
break;
case 3:
//
// Remake inquiryData.
//
RtlCopyMemory(inquiryData, savedInquiryData, 32);
//
// Need to strip off all but the first character of revision.
//
inquiryData[25] = '\0';
sprintf(hwId, "%s", "MPIO\\");
index = hwId;
index += strlen(hwId);
RtlMoveMemory(index, inquiryData, strlen(inquiryData));
break;
case 4:
//
// This is like 3, but no SCSI\ in front.
//
sprintf(hwId, "%s", inquiryData);
break;
case 5:
//
// Only the GenDisk
//
sprintf(hwId, "%s", "GenDisk");
break;
default:
break;
}
//
// Allocate and build the hwString entry.
//
hwStrings[i] = ExAllocatePool(PagedPool, strlen(hwId) + sizeof(UCHAR));
if (hwStrings == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(hwStrings[i], strlen(hwId) + sizeof(UCHAR));
RtlCopyMemory(hwStrings[i], hwId, strlen(hwId));
MPDebugPrint((2,
"MPathHwIds: Hw String[%x] - %s\n",
i,
hwStrings[i]));
}
status = STATUS_SUCCESS;
//
// Convert the hwString entry into the unicode version that the caller wants.
//
for (i = 0, bufferLength = 0; i < 6; i++) {
bufferLength += strlen(hwStrings[i]) * sizeof(WCHAR);
//
// Make room for the NULL after the string.
//
bufferLength += sizeof(UNICODE_NULL);
}
UnicodeString->Length = (USHORT)bufferLength;
//
// Add room for the final terminating NULL
//
bufferLength += sizeof(UNICODE_NULL);
//
// Allocate the buffer.
//
UnicodeString->Buffer = ExAllocatePool(PagedPool, bufferLength);
if (UnicodeString->Buffer) {
RtlZeroMemory(UnicodeString->Buffer, bufferLength);
UnicodeString->MaximumLength = (USHORT)bufferLength;
unicodeEntry = *UnicodeString;
for (i = 0; i < 6; i++) {
//
// Convert ascii to ansi.
//
RtlInitAnsiString(&ansiString, hwStrings[i]);
status = RtlAnsiStringToUnicodeString(&unicodeEntry,
&ansiString,
FALSE);
if (!NT_SUCCESS(status)) {
break;
}
//
// update the unicode fields.
//
((PSTR)unicodeEntry.Buffer) += unicodeEntry.Length + sizeof(WCHAR);
unicodeEntry.MaximumLength -= unicodeEntry.Length + sizeof(WCHAR);
}
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
return status;
}
NTSTATUS
MPIODeviceId(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN OUT PUNICODE_STRING UnicodeString
)
//
// Need to build up a name like MPATH#<DevType>&VenXXX&ProdXXX&RevXXX
//
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
ANSI_STRING ansiIdString;
CHAR deviceStrings[4][255];
UCHAR deviceId[256];
PUCHAR scsiField;
PUCHAR currentId;
NTSTATUS status;
ULONG i;
ULONG deviceIndex = 0;
RtlZeroMemory(deviceId, 256);
currentId = deviceId;
deviceDescriptor = diskExtension->DeviceDescriptor;
scsiField = (PUCHAR)deviceDescriptor;
//
// Preload the deviceId with MPIO\<deviceType>
//
sprintf(currentId, "%s", "MPIO\\Disk&Ven_");
//
// Push the current pointer past the above field.
//
currentId += strlen(currentId);
//
// Push the scsiField pointer to that of the vendor id.
//
ASSERT(deviceDescriptor->VendorIdOffset != 0);
ASSERT(deviceDescriptor->ProductIdOffset != 0);
ASSERT(deviceDescriptor->ProductRevisionOffset != 0);
(ULONG_PTR)scsiField += deviceDescriptor->VendorIdOffset;
//
// Copy in the Vendor name.
//
for (i = 0; i < VENDOR_ID_LENGTH; i++) {
*currentId = *scsiField;
currentId++;
scsiField++;
}
*currentId = '\0';
currentId++;
//
// Bump the scsiField to that of the product id.
//
scsiField = (PUCHAR)deviceDescriptor;
(ULONG_PTR)scsiField += deviceDescriptor->ProductIdOffset;
//
// Remove any trailing spaces
//
while (*currentId == ' ' || *currentId == '\0') {
currentId--;
}
currentId++;
//
// Jam in &Prod
//
sprintf(currentId, "%s", "&Prod_");
currentId += strlen(currentId);
//
// Copy in the Product Id.
//
for (i = 0; i < PRODUCT_ID_LENGTH; i++) {
*currentId = *scsiField;
currentId++;
scsiField++;
}
*currentId = '\0';
currentId++;
//
// Remove any trailing spaces
//
while (*currentId == ' ' || *currentId == '\0') {
currentId--;
}
currentId++;
//
// Handle the revision.
//
scsiField = (PUCHAR)deviceDescriptor;
(ULONG_PTR)scsiField += deviceDescriptor->ProductRevisionOffset;
sprintf(currentId, "%s", "&Rev_");
currentId += strlen(currentId);
for (i = 0; i < REVISION_LENGTH; i++) {
*currentId = *scsiField;
currentId++;
scsiField++;
}
*currentId = '\0';
currentId++;
//
// Remove any trailing spaces
//
while (*currentId == ' ' || *currentId == '\0') {
currentId--;
}
//
// Run through and fixup spaces.
//
currentId = deviceId;
while (*currentId != '\0') {
if (*currentId == ' ') {
*currentId = '_';
}
currentId++;
}
MPDebugPrint((2,
"MPathDeviceId - %s\n",
deviceId));
//
// Finally make the unicode string that will be returned to PnP.
//
RtlInitAnsiString(&ansiIdString,deviceId);
status = RtlAnsiStringToUnicodeString(UnicodeString,
&ansiIdString,
TRUE);
return status;
}
NTSTATUS
MPIOPdoQueryId(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine handles QueryId requests sent to the PDO (PseudoDisks).
Arguments:
DeviceObject
Irp
Return Value:
NTSTATUS
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status;
UNICODE_STRING unicodeID;
UNICODE_STRING unicodeString;
ANSI_STRING ansiString;
UNICODE_STRING unicodeIndex;
ULONG bufferLength = 0;
UCHAR deviceId[256];
RtlZeroMemory(deviceId, 256);
switch (irpStack->Parameters.QueryId.IdType) {
case BusQueryHardwareIDs: {
status = MPIOHardwareIDs(DeviceObject,
Irp,
&unicodeString);
if (NT_SUCCESS(status)) {
//
// Set the hardware ID buffer.
//
Irp->IoStatus.Information = (ULONG_PTR)unicodeString.Buffer;
}
break;
}
case BusQueryDeviceID: {
status = MPIODeviceId(DeviceObject,
Irp,
&unicodeString);
if (NT_SUCCESS(status)) {
Irp->IoStatus.Information = (ULONG_PTR)unicodeString.Buffer;
}
break;
}
case BusQueryInstanceID: {
PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
ULONG serialNumberLength;
PUCHAR serialNumber;
UCHAR fakeSerialNumber[20];
ANSI_STRING ansiSerialNumber;
deviceDescriptor = diskExtension->DeviceDescriptor;
serialNumber = (PUCHAR)deviceDescriptor;
//
// If the device has no serial number, need to make something up.
// TODO
//
if (deviceDescriptor->SerialNumberOffset == (ULONG)-1) {
RtlZeroMemory(fakeSerialNumber, 20);
sprintf(fakeSerialNumber, "00%d", diskExtension->DeviceOrdinal);
serialNumber = fakeSerialNumber;
} else {
//
// Move serialNumber to the correct position in RawData.
//
(ULONG_PTR)serialNumber += deviceDescriptor->SerialNumberOffset;
}
//
// Get it's length.
//
serialNumberLength = strlen(serialNumber);
//
// Build the ansi string based on the serial number information.
// Then convert to unicode.
//
RtlInitAnsiString(&ansiSerialNumber, serialNumber);
RtlAnsiStringToUnicodeString(&unicodeString, &ansiSerialNumber, TRUE);
status = STATUS_SUCCESS;
Irp->IoStatus.Information = (ULONG_PTR)unicodeString.Buffer;
break;
}
case BusQueryCompatibleIDs: {
UNICODE_STRING unicodeString;
ULONG i;
PSTR deviceStrings[] = {"SCSI\\Disk", "SCSI\\RAW", NULL};
//
// Determine length needed for the unicode buffer.
//
for (i = 0; deviceStrings[i] != NULL; i++) {
bufferLength += strlen(deviceStrings[i]) * sizeof(WCHAR);
//
// Make room for the NULL after the string.
//
bufferLength += sizeof(UNICODE_NULL);
}
unicodeString.Length = (USHORT)bufferLength;
//
// Add room for the final terminating NULL
//
bufferLength += sizeof(UNICODE_NULL);
//
// Allocate the buffer.
//
unicodeString.Buffer = ExAllocatePool(PagedPool, bufferLength);
if (unicodeString.Buffer == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
} else {
//
// Finish initing the unicode string.
//
RtlZeroMemory(unicodeString.Buffer, bufferLength);
unicodeString.MaximumLength = (USHORT)bufferLength;
//
// Set the index string to the front of the real unicode string.
// This index will be pushed along so that the creation of the MULTI string
// can be done.
//
unicodeIndex = unicodeString;
for (i = 0, status = STATUS_SUCCESS; deviceStrings[i] != NULL; i++) {
RtlInitAnsiString(&ansiString, deviceStrings[i]);
status = RtlAnsiStringToUnicodeString(&unicodeIndex,
&ansiString,
FALSE);
if (NT_SUCCESS(status)) {
//
// push the index past the last unicode string built.
//
((PSTR)unicodeIndex.Buffer) += unicodeIndex.Length + sizeof(WCHAR);
//
// Ensure the max length is correct.
//
unicodeIndex.MaximumLength -= unicodeIndex.Length + sizeof(WCHAR);
} else {
//
// TODO: Something.
//
break;
}
}
if (NT_SUCCESS(status)) {
Irp->IoStatus.Information = (ULONG_PTR)unicodeString.Buffer;
}
}
break;
}
default:
status = Irp->IoStatus.Status;
Irp->IoStatus.Information = 0;
break;
}
return status;
}
NTSTATUS
MPIOPdoDeviceUsage(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
//
// TODO
//
return STATUS_SUCCESS;
}