private SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER BuildSCSIPassThroughStructure(byte[] commandBytes, LogicalUnit logicalUnit, byte[] data) { SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER scsi = null; scsi = new SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER(); scsi.Spt.Length = (ushort)Marshal.SizeOf(scsi.Spt); scsi.Spt.PathId = logicalUnit.PathId; scsi.Spt.TargetId = logicalUnit.TargetId; scsi.Spt.Lun = logicalUnit.TargetLun; scsi.Spt.CdbLength = (byte)commandBytes.Length; scsi.Spt.Cdb = commandBytes; if (data != null && data.Length > 0) { // DATA OUT (from initiator to target, WRITE) scsi.Spt.DataIn = (byte)SCSIDataDirection.Out; scsi.Spt.DataTransferLength = (uint)data.Length; scsi.Spt.DataBuffer = Marshal.AllocHGlobal((int)scsi.Spt.DataTransferLength); Marshal.Copy(data, 0, scsi.Spt.DataBuffer, data.Length); } else { // DATA IN (to initiator from target, READ) scsi.Spt.DataIn = (byte)SCSICommandParser.GetDataDirection(commandBytes); if ((SCSIDataDirection)scsi.Spt.DataIn == SCSIDataDirection.In) { scsi.Spt.DataTransferLength = GetDataInTransferLength(commandBytes, logicalUnit); scsi.Spt.DataBuffer = Marshal.AllocHGlobal((int)scsi.Spt.DataTransferLength); } else { scsi.Spt.DataTransferLength = 0; // No data! scsi.Spt.DataBuffer = IntPtr.Zero; } } Log(Severity.Verbose, "SCSI Command: {0}, Data Length: {1}, Transfer Direction: {2}, Transfer Length: {3}, LUN: {4}", (SCSIOpCodeName)commandBytes[0], data.Length, (SCSIDataDirection)scsi.Spt.DataIn, scsi.Spt.DataTransferLength, logicalUnit.AssociatedLun); scsi.Spt.TimeOutValue = SCSI_TIMEOUT; scsi.Spt.SenseInfoOffset = (uint)Marshal.OffsetOf(typeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER), "Sense"); scsi.Spt.SenseInfoLength = (byte)scsi.Sense.Length; return(scsi); }
/// <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); }