#include "mpio.h" #include #include 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 <"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#&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\ // 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; }