Beispiel #1
0
        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);
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        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);
            }
        }
Beispiel #4
0
        // 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);
            }
        }
Beispiel #5
0
        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);
            }
        }
Beispiel #6
0
        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);
 }
Beispiel #8
0
        /// <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);
        }
Beispiel #9
0
        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);
        }