123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Runtime.InteropServices;
- using Microsoft.Win32.SafeHandles;
- using DiskAccessLibrary;
- using Utilities;
- namespace SCSI.Win32
- {
-
-
- public class SPTITarget : SCSITarget
- {
- public const int IOCTL_SCSI_GET_INQUIRY_DATA = 0x4100c;
- public const int IOCTL_SCSI_PASS_THROUGH_DIRECT = 0x4D014;
- public const int SCSI_TIMEOUT = 60;
- public event EventHandler<LogEntry> OnLogEntry;
- private string m_path;
- private SafeFileHandle m_handle;
- private bool m_emulateReportLUNs;
- private LogicalUnitManager m_logicalUnitManager = new LogicalUnitManager();
- public SPTITarget(string path) : this(path, false)
- {
- }
- public SPTITarget(string path, bool emulateReportLUNs)
- {
- m_path = path;
- m_handle = HandleUtils.GetFileHandle(m_path, FileAccess.ReadWrite, ShareMode.None);
- m_emulateReportLUNs = emulateReportLUNs;
- }
- public void Close()
- {
- m_handle.Close();
- }
- [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
- public static extern bool DeviceIoControl(SafeHandle hDevice, uint dwIoControlCode, IntPtr lpInBuffer, uint nInBufferSize, IntPtr lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped);
-
-
-
- public override SCSIStatusCodeName ExecuteCommand(byte[] commandBytes, LUNStructure lun, byte[] data, out byte[] response)
- {
-
- 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;
- }
-
- Array.Resize(ref commandBytes, SCSI_PASS_THROUGH_DIRECT.CdbBufferLength);
-
- 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);
-
- SCSIStatusCodeName status;
- if (!DeviceIoControl(m_handle, IOCTL_SCSI_PASS_THROUGH_DIRECT, inBuffer, size, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero))
- {
-
-
-
-
-
- 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
- {
-
- 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 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 (!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 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)
- {
-
- 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
- {
-
- 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;
- 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;
- }
- private uint GetDataInTransferLength(byte[] commandBytes, LogicalUnit logicalUnit)
- {
- switch ((SCSIOpCodeName)commandBytes[0])
- {
- case SCSIOpCodeName.Read16:
- case SCSIOpCodeName.ReadReverse16:
- case SCSIOpCodeName.Read6:
- case SCSIOpCodeName.ReadReverse6:
- case SCSIOpCodeName.Read10:
- case SCSIOpCodeName.Read12:
- {
- 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);
- }
- }
-
- 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 InterceptModeSelect6(LogicalUnit logicalUnit, byte[] commandBytes, byte[] data)
- {
- ModeParameterHeader6 header = new ModeParameterHeader6(data, 0);
- if (header.BlockDescriptorLength == ShortLBAModeParameterBlockDescriptor.Length)
- {
- ShortLBAModeParameterBlockDescriptor descriptor = new ShortLBAModeParameterBlockDescriptor(data, ModeParameterHeader6.Length);
- logicalUnit.BlockSize = descriptor.LogicalBlockLength;
- Log(Severity.Verbose, "LUN: {0}, BlockSize updated to {1}", logicalUnit.AssociatedLun, logicalUnit.BlockSize);
- }
- }
- 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);
- }
- }
- 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 void InterceptModeSense10(LogicalUnit logicalUnit, byte[] commandBytes, byte[] response)
- {
- ModeParameterHeader10 header = new ModeParameterHeader10(response, 0);
- if (header.BlockDescriptorLength == ShortLBAModeParameterBlockDescriptor.Length)
- {
- ShortLBAModeParameterBlockDescriptor descriptor = new ShortLBAModeParameterBlockDescriptor(response, 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(commandBytes, ModeParameterHeader10.Length);
- logicalUnit.BlockSize = descriptor.LogicalBlockLength;
- Log(Severity.Verbose, "LUN: {0}, BlockSize updated to {1}", logicalUnit.AssociatedLun, logicalUnit.BlockSize);
- }
- }
- private void InterceptReadCapacity10(LogicalUnit logicalUnit, byte[] commandBytes, byte[] response)
- {
- ReadCapacity10Parameter parameter = new ReadCapacity10Parameter(response);
- logicalUnit.BlockSize = parameter.BlockLengthInBytes;
- Log(Severity.Verbose, "LUN: {0}, BlockSize updated to {1}", logicalUnit.AssociatedLun, logicalUnit.BlockSize);
- }
- 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);
- }
- public void Log(Severity severity, string message)
- {
-
- EventHandler<LogEntry> handler = OnLogEntry;
- if (handler != null)
- {
- handler(this, new LogEntry(DateTime.Now, severity, "SPTI Target", message));
- }
- }
- public void Log(Severity severity, string message, params object[] args)
- {
- Log(severity, String.Format(message, args));
- }
- }
- }
|