Esempio n. 1
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);
        }
Esempio n. 2
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);
        }