Example #1
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);
        }
Example #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);
        }