123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- /* Copyright (C) 2012-2016 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
- *
- * You can redistribute this program and/or modify it under the terms of
- * the GNU Lesser Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- */
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Text;
- using System.Runtime.InteropServices;
- using DiskAccessLibrary;
- using Utilities;
- namespace ISCSI.Server
- {
- public class TargetResponseHelper
- {
- internal static List<ISCSIPDU> GetSCSIResponsePDU(SCSICommandPDU command, ISCSITarget target, SessionParameters session, ConnectionParameters connection)
- {
- ushort LUN = command.LUN;
- // We return either SCSIResponsePDU or List<SCSIDataInPDU>
- List<ISCSIPDU> responseList = new List<ISCSIPDU>();
-
- string connectionIdentifier = StateObject.GetConnectionIdentifier(session, connection);
- if (command.Write && command.DataSegmentLength < command.ExpectedDataTransferLength)
- {
- uint transferTag = session.GetNextTransferTag();
- // Store segment (we only execute the command after receiving all of its data)
- byte[] commandData = new byte[command.ExpectedDataTransferLength];
- Array.Copy(command.Data, 0, commandData, 0, command.DataSegmentLength);
- connection.Transfers.Add(transferTag, new KeyValuePair<ulong, uint>(command.CommandDescriptorBlock.LogicalBlockAddress64, command.ExpectedDataTransferLength));
- connection.TransferData.Add(transferTag, commandData);
- // Send R2T
- ReadyToTransferPDU response = new ReadyToTransferPDU();
- response.InitiatorTaskTag = command.InitiatorTaskTag;
- response.R2TSN = 0; // R2Ts are sequenced per command and must start with 0 for each new command;
- response.TargetTransferTag = transferTag;
- response.BufferOffset = command.DataSegmentLength;
- response.DesiredDataTransferLength = Math.Min((uint)connection.TargetMaxRecvDataSegmentLength, command.ExpectedDataTransferLength - response.BufferOffset);
- // We store the next R2TSN to be used
- session.NextR2TSN.Add(transferTag, 1);
- responseList.Add(response);
- return responseList;
- }
- byte[] scsiResponse;
- SCSIStatusCodeName status = target.ExecuteCommand(command.CommandDescriptorBlock, command.LUN, command.Data, out scsiResponse);
- if (!command.Read)
- {
- SCSIResponsePDU response = new SCSIResponsePDU();
- response.InitiatorTaskTag = command.InitiatorTaskTag;
- response.Status = status;
- response.Data = scsiResponse;
- responseList.Add(response);
- }
- else if (scsiResponse.Length <= connection.InitiatorMaxRecvDataSegmentLength)
- {
- SCSIDataInPDU response = new SCSIDataInPDU();
- response.InitiatorTaskTag = command.InitiatorTaskTag;
- response.Status = status;
- response.StatusPresent = true;
- response.Final = true;
- response.Data = scsiResponse;
- EnforceExpectedDataTransferLength(response, command.ExpectedDataTransferLength);
- responseList.Add(response);
- }
- else // we have to split the response to multiple Data-In PDUs
- {
- int bytesLeftToSend = scsiResponse.Length;
- uint dataSN = 0;
- while (bytesLeftToSend > 0)
- {
- int dataSegmentLength = Math.Min(connection.InitiatorMaxRecvDataSegmentLength, bytesLeftToSend);
- int dataOffset = scsiResponse.Length - bytesLeftToSend;
-
- SCSIDataInPDU response = new SCSIDataInPDU();
- response.InitiatorTaskTag = command.InitiatorTaskTag;
- if (bytesLeftToSend == dataSegmentLength)
- {
- // last Data-In PDU
- response.Status = status;
- response.StatusPresent = true;
- response.Final = true;
- }
- response.BufferOffset = (uint)dataOffset;
- response.DataSN = dataSN;
- dataSN++;
- response.Data = new byte[dataSegmentLength];
- Array.Copy(scsiResponse, dataOffset, response.Data, 0, dataSegmentLength);
- responseList.Add(response);
- bytesLeftToSend -= dataSegmentLength;
- }
- }
- return responseList;
- }
- internal static ISCSIPDU GetSCSIDataOutResponsePDU(SCSIDataOutPDU request, ISCSITarget target, SessionParameters session, ConnectionParameters connection)
- {
- string connectionIdentifier = StateObject.GetConnectionIdentifier(session, connection);
- if (connection.Transfers.ContainsKey(request.TargetTransferTag))
- {
- ushort LUN = (ushort)request.LUN;
-
- Disk disk = target.Disks[LUN];
- uint offset = request.BufferOffset;
- uint totalLength = connection.Transfers[request.TargetTransferTag].Value;
- // Store segment (we only execute the command after receiving all of its data)
- byte[] commandData = connection.TransferData[request.TargetTransferTag];
- Array.Copy(request.Data, 0, commandData, offset, request.DataSegmentLength);
-
- ISCSIServer.Log(String.Format("[{0}][GetSCSIDataOutResponsePDU] Buffer offset: {1}, Total length: {2}", connectionIdentifier, offset, totalLength));
- if (offset + request.DataSegmentLength == totalLength)
- {
- // Last Data-out PDU
- ISCSIServer.Log("[{0}][GetSCSIDataOutResponsePDU] Last Data-out PDU", connectionIdentifier);
-
- long sectorIndex = (long)connection.Transfers[request.TargetTransferTag].Key;
- SCSIResponsePDU response = new SCSIResponsePDU();
- response.InitiatorTaskTag = request.InitiatorTaskTag;
- response.Status = target.Write(request.LUN, sectorIndex, commandData, out response.Data);
- connection.Transfers.Remove(request.TargetTransferTag);
- connection.TransferData.Remove(request.TargetTransferTag);
- session.NextR2TSN.Remove(request.TargetTransferTag);
- return response;
- }
- else
- {
- // Send R2T
- ReadyToTransferPDU response = new ReadyToTransferPDU();
- response.InitiatorTaskTag = request.InitiatorTaskTag;
- response.TargetTransferTag = request.TargetTransferTag;
- response.R2TSN = session.GetNextR2TSN(request.TargetTransferTag);
- response.BufferOffset = offset + request.DataSegmentLength; // where we left off
- response.DesiredDataTransferLength = Math.Min((uint)connection.TargetMaxRecvDataSegmentLength, totalLength - response.BufferOffset);
-
- return response;
- }
-
- }
- else
- {
- ISCSIServer.Log("[{0}][GetSCSIDataOutResponsePDU] Unfamiliar TargetTransferTag", connectionIdentifier);
- SCSIResponsePDU response = new SCSIResponsePDU();
- response.InitiatorTaskTag = request.InitiatorTaskTag;
- response.Status = SCSIStatusCodeName.CheckCondition;
- response.Data = SCSITarget.FormatSenseData(SenseDataParameter.GetIllegalRequestSenseData());
- return response;
- }
- }
- public static void EnforceExpectedDataTransferLength(SCSIDataInPDU response, uint expectedDataTransferLength)
- {
- if (response.Data.Length > expectedDataTransferLength)
- {
- response.ResidualOverflow = true;
- response.ResidualCount = (uint)(expectedDataTransferLength - response.Data.Length);
- response.Data = ByteReader.ReadBytes(response.Data, 0, (int)expectedDataTransferLength);
- }
- else if (response.Data.Length < expectedDataTransferLength)
- {
- response.ResidualUnderflow = true;
- response.ResidualCount = (uint)(expectedDataTransferLength - response.Data.Length);
- }
- }
- }
- }
|