private SCSIStatusCodeName ReportLUNs(out byte[] response) { uint bytesReturned; uint outBufferLength = 4096; IntPtr outBuffer = Marshal.AllocHGlobal((int)outBufferLength); SCSIStatusCodeName status; if (!DeviceIoControl(m_handle, IOCTL_SCSI_GET_INQUIRY_DATA, IntPtr.Zero, 0, outBuffer, outBufferLength, out bytesReturned, IntPtr.Zero)) { Win32Error lastError = (Win32Error)Marshal.GetLastWin32Error(); Log(Severity.Error, "DeviceIoControl/IOCTL_SCSI_GET_INQUIRY_DATA error: {0}, Device path: {1}", lastError, m_path); response = VirtualSCSITarget.FormatSenseData(SenseDataParameter.GetIllegalRequestUnsupportedCommandCodeSenseData()); status = SCSIStatusCodeName.CheckCondition; } else { List <SCSI_INQUIRY_DATA> devices = SCSI_ADAPTER_BUS_INFO.GetInquiryDataForAllDevices(outBuffer); ReportLUNsParameter parameter = new ReportLUNsParameter(); foreach (SCSI_INQUIRY_DATA device in devices) { // If the device has been claimed by a class driver then passthrough IOCTLs must go through the class driver if (!device.DeviceClaimed) { PeripheralDeviceType deviceType = (PeripheralDeviceType)(device.InquiryData[0] & 0x1F); if (deviceType == PeripheralDeviceType.DirectAccessBlockDevice | deviceType == PeripheralDeviceType.SequentialAccessDevice | deviceType == PeripheralDeviceType.CDRomDevice) { byte?associatedLUN = m_logicalUnitManager.FindAssociatedLUN(device.PathId, device.TargetId, device.Lun); if (!associatedLUN.HasValue) { associatedLUN = m_logicalUnitManager.FindUnusedLUN(); LogicalUnit logicalUnit = new LogicalUnit(); logicalUnit.AssociatedLun = associatedLUN.Value; logicalUnit.PathId = device.PathId; logicalUnit.TargetId = device.TargetId; logicalUnit.TargetLun = device.Lun; logicalUnit.DeviceType = deviceType; m_logicalUnitManager.AddLogicalUnit(logicalUnit); Log(Severity.Verbose, "Assigned virtual LUN {0} to device PathId: {1}, TargetId: {2}, LUN: {3}", associatedLUN.Value, device.PathId, device.TargetId, device.Lun); } if (!associatedLUN.HasValue) { throw new NotImplementedException("The maximum number of LUNs supported has been reached"); } parameter.LUNList.Add(associatedLUN.Value); } } } response = parameter.GetBytes(); Log(Severity.Verbose, "DeviceIoControl/IOCTL_SCSI_GET_INQUIRY_DATA reported {0} usable devices", parameter.LUNList.Count); status = SCSIStatusCodeName.Good; } Marshal.FreeHGlobal(outBuffer); return(status); }
/// <summary> /// This takes the iSCSI command and forwards it to a SCSI Passthrough device. It then returns the response. /// </summary> public override SCSIStatusCodeName ExecuteCommand(byte[] commandBytes, LUNStructure lun, byte[] data, out byte[] response) { // SPTI only supports up to 16 byte CDBs if (commandBytes.Length > SCSI_PASS_THROUGH_DIRECT.CdbBufferLength) { response = VirtualSCSITarget.FormatSenseData(SenseDataParameter.GetIllegalRequestUnsupportedCommandCodeSenseData()); return(SCSIStatusCodeName.CheckCondition); } if (commandBytes[0] == (byte)SCSIOpCodeName.ReportLUNs) { if (m_emulateReportLUNs) { Log(Severity.Verbose, "SCSI Command: ReportLUNs"); ReportLUNsParameter parameter = new ReportLUNsParameter(); parameter.LUNList.Add(0); response = parameter.GetBytes(); if (m_logicalUnitManager.FindLogicalUnit(0) == null) { m_logicalUnitManager.AddLogicalUnit(new LogicalUnit()); } return(SCSIStatusCodeName.Good); } else { return(ReportLUNs(out response)); } } LogicalUnit logicalUnit = m_logicalUnitManager.FindLogicalUnit((byte)lun); if (logicalUnit == null) { response = VirtualSCSITarget.FormatSenseData(SenseDataParameter.GetIllegalRequestInvalidLUNSenseData()); return(SCSIStatusCodeName.CheckCondition); } // Pad all CDBs to 16 bytes Array.Resize(ref commandBytes, SCSI_PASS_THROUGH_DIRECT.CdbBufferLength); // Build SCSI Passthrough structure SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER scsi = BuildSCSIPassThroughStructure(commandBytes, logicalUnit, data); if (scsi == null) { response = VirtualSCSITarget.FormatSenseData(SenseDataParameter.GetIllegalRequestUnsupportedCommandCodeSenseData()); return(SCSIStatusCodeName.CheckCondition); } uint bytesReturned; IntPtr inBuffer = inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(scsi)); uint size = (uint)Marshal.SizeOf(scsi); Marshal.StructureToPtr(scsi, inBuffer, true); // Forward SCSI command to target SCSIStatusCodeName status; if (!DeviceIoControl(m_handle, IOCTL_SCSI_PASS_THROUGH_DIRECT, inBuffer, size, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero)) { // Notes: // 1. DeviceIoControl will return ERROR_INVALID_HANDLE under Windows Vista or later if not running as administrator. // 2. If a device class driver has claimed the device then passthrough IOCTLs must go through the device class driver. // Sending IOCTLs to the port driver will return ERROR_INVALID_FUNCTION in such cases. // To work with an HBA one can disable the disk drivers of disks connected to that HBA. Win32Error lastError = (Win32Error)Marshal.GetLastWin32Error(); Log(Severity.Error, "DeviceIoControl/IOCTL_SCSI_PASS_THROUGH_DIRECT error: {0}, Device path: {1}", lastError, m_path); response = VirtualSCSITarget.FormatSenseData(SenseDataParameter.GetIllegalRequestUnsupportedCommandCodeSenseData()); status = SCSIStatusCodeName.CheckCondition; } else { Marshal.PtrToStructure(inBuffer, scsi); status = (SCSIStatusCodeName)scsi.Spt.ScsiStatus; if (status != SCSIStatusCodeName.Good) { Log(Severity.Verbose, "SCSI Status: {0}, Sense: {1}", status, BitConverter.ToString(scsi.Sense)); response = new byte[scsi.Sense.Length + 2]; BigEndianWriter.WriteUInt16(response, 0, (ushort)scsi.Sense.Length); ByteWriter.WriteBytes(response, 2, scsi.Sense); } else { if (scsi.Spt.DataTransferLength > 0) { if (scsi.Spt.DataIn == (byte)SCSIDataDirection.In) { response = new byte[scsi.Spt.DataTransferLength]; Marshal.Copy(scsi.Spt.DataBuffer, response, 0, response.Length); } else { response = new byte[0]; } Log(Severity.Verbose, "SCSI Status: {0}, Response Length: {1}", status, response.Length); if (commandBytes[0] == (byte)SCSIOpCodeName.Inquiry) { InterceptInquiry(logicalUnit, commandBytes, response); } else if (commandBytes[0] == (byte)SCSIOpCodeName.ModeSelect6) { InterceptModeSelect6(logicalUnit, commandBytes, data); } else if (commandBytes[0] == (byte)SCSIOpCodeName.ModeSense6) { InterceptModeSense6(logicalUnit, commandBytes, response); } else if (commandBytes[0] == (byte)SCSIOpCodeName.ReadCapacity10) { InterceptReadCapacity10(logicalUnit, commandBytes, response); } else if (commandBytes[0] == (byte)SCSIOpCodeName.ModeSelect10) { InterceptModeSelect10(logicalUnit, commandBytes, data); } else if (commandBytes[0] == (byte)SCSIOpCodeName.ModeSense10) { InterceptModeSense10(logicalUnit, commandBytes, response); } else if (commandBytes[0] == (byte)SCSIOpCodeName.ServiceActionIn16 && commandBytes[1] == (byte)ServiceAction.ReadCapacity16) { InterceptReadCapacity16(logicalUnit, commandBytes, response); } } else { // SPTI request was GOOD, no data in response buffer. response = new byte[0]; Log(Severity.Verbose, "SCSI Status: {0}", status); } } } Marshal.FreeHGlobal(inBuffer); if (scsi.Spt.DataBuffer != IntPtr.Zero) { Marshal.FreeHGlobal(scsi.Spt.DataBuffer); } return(status); }