private void InterceptReadCapacity16(LogicalUnit logicalUnit, byte[] commandBytes, byte[] response) { ReadCapacity16Parameter parameter = new ReadCapacity16Parameter(response); logicalUnit.BlockSize = parameter.BlockLengthInBytes; Log(Severity.Verbose, "LUN: {0}, BlockSize updated to {1}", logicalUnit.AssociatedLun, logicalUnit.BlockSize); }
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); }
private void InterceptModeSense6(LogicalUnit logicalUnit, byte[] commandBytes, byte[] response) { ModeParameterHeader6 header = new ModeParameterHeader6(response, 0); if (header.BlockDescriptorLength == ShortLBAModeParameterBlockDescriptor.Length) { ShortLBAModeParameterBlockDescriptor descriptor = new ShortLBAModeParameterBlockDescriptor(response, ModeParameterHeader6.Length); logicalUnit.BlockSize = descriptor.LogicalBlockLength; Log(Severity.Verbose, "LUN: {0}, BlockSize updated to {1}", logicalUnit.AssociatedLun, logicalUnit.BlockSize); } }
// Intercept Inquiry and update the peripheral device type private void InterceptInquiry(LogicalUnit logicalUnit, byte[] commandBytes, byte[] response) { bool EVPD = ((commandBytes[1] & 0x01) != 0); byte pageCode = commandBytes[2]; if (!EVPD && pageCode == 0) { logicalUnit.DeviceType = (PeripheralDeviceType)(response[0] & 0x1F); Log(Severity.Verbose, "LUN: {0}, DeviceType updated to {1}", logicalUnit.AssociatedLun, logicalUnit.DeviceType); } }
private void InterceptModeSelect10(LogicalUnit logicalUnit, byte[] commandBytes, byte[] data) { ModeParameterHeader10 header = new ModeParameterHeader10(data, 0); if (header.BlockDescriptorLength == ShortLBAModeParameterBlockDescriptor.Length) { ShortLBAModeParameterBlockDescriptor descriptor = new ShortLBAModeParameterBlockDescriptor(data, ModeParameterHeader10.Length); logicalUnit.BlockSize = descriptor.LogicalBlockLength; Log(Severity.Verbose, "LUN: {0}, BlockSize updated to {1}", logicalUnit.AssociatedLun, logicalUnit.BlockSize); } else if (header.BlockDescriptorLength == LongLBAModeParameterBlockDescriptor.Length) { LongLBAModeParameterBlockDescriptor descriptor = new LongLBAModeParameterBlockDescriptor(data, ModeParameterHeader10.Length); logicalUnit.BlockSize = descriptor.LogicalBlockLength; Log(Severity.Verbose, "LUN: {0}, BlockSize updated to {1}", logicalUnit.AssociatedLun, logicalUnit.BlockSize); } }
private uint GetDataInTransferLength(byte[] commandBytes, LogicalUnit logicalUnit) { switch ((SCSIOpCodeName)commandBytes[0]) { case SCSIOpCodeName.Read16: // DATA_IN (12-14) case SCSIOpCodeName.ReadReverse16: // DATA_IN (12-14) case SCSIOpCodeName.Read6: // DATA_IN (2-4) case SCSIOpCodeName.ReadReverse6: // DATA_IN (2-4) case SCSIOpCodeName.Read10: // DATA_IN (7-8) case SCSIOpCodeName.Read12: // DATA_IN (6-9) { if (logicalUnit.BlockSize == null) { throw new NotSupportedException("Command Sequence Not Supported!"); } return(SCSICommandParser.GetDeviceReadTransferLength(commandBytes, logicalUnit.DeviceType, logicalUnit.BlockSize.Value)); } default: return(SCSICommandParser.GetCDBTransferLength(commandBytes, logicalUnit.DeviceType)); } }
public void AddLogicalUnit(LogicalUnit logicalUnit) { m_luns.Add(logicalUnit.AssociatedLun, logicalUnit); }
/// <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); }
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); }