internal static ISCSIPDU GetSCSIDataOutResponsePDU(SCSIDataOutPDU request, ISCSITarget target, SessionParameters session, ConnectionParameters connection) { string connectionIdentifier = StateObject.GetConnectionIdentifier(session, connection); TransferEntry transfer = connection.GetTransferEntry(request.TargetTransferTag); if (transfer == null) { ISCSIServer.Log("[{0}][GetSCSIDataOutResponsePDU] Invalid TargetTransferTag {1}", connectionIdentifier, request.TargetTransferTag); RejectPDU reject = new RejectPDU(); reject.InitiatorTaskTag = request.InitiatorTaskTag; reject.Reason = RejectReason.InvalidPDUField; reject.Data = ByteReader.ReadBytes(request.GetBytes(), 0, 48); return(reject); } ushort LUN = (ushort)request.LUN; Disk disk = target.Disks[LUN]; uint offset = request.BufferOffset; uint totalLength = (uint)transfer.CommandDataBuffer.Length; // Store segment (we only execute the command after receiving all of its data) Array.Copy(request.Data, 0, transfer.CommandDataBuffer, 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); byte[] scsiResponse; SCSIStatusCodeName status = target.ExecuteCommand(transfer.CommandBytes, request.LUN, transfer.CommandDataBuffer, out scsiResponse); SCSIResponsePDU response = new SCSIResponsePDU(); response.InitiatorTaskTag = request.InitiatorTaskTag; response.Status = status; response.Data = scsiResponse; connection.RemoveTransfer(request.TargetTransferTag); return(response); } else { // Send R2T ReadyToTransferPDU response = new ReadyToTransferPDU(); response.InitiatorTaskTag = request.InitiatorTaskTag; response.TargetTransferTag = request.TargetTransferTag; response.R2TSN = transfer.NextR2NSN; response.BufferOffset = offset + request.DataSegmentLength; // where we left off response.DesiredDataTransferLength = Math.Min((uint)connection.TargetMaxRecvDataSegmentLength, totalLength - response.BufferOffset); transfer.NextR2NSN++; return(response); } }
internal static List <ReadyToTransferPDU> GetReadyToTransferPDUs(SCSIDataOutPDU request, ConnectionParameters connection, out List <SCSICommandPDU> commandsToExecute) { List <ReadyToTransferPDU> responseList = new List <ReadyToTransferPDU>(); commandsToExecute = new List <SCSICommandPDU>(); ISCSISession session = connection.Session; TransferEntry transfer = connection.GetTransferEntry(request.TargetTransferTag); if (transfer == null) { throw new InvalidTargetTransferTagException(request.TargetTransferTag); } uint offset = request.BufferOffset; uint totalLength = (uint)transfer.Command.ExpectedDataTransferLength; // Store segment (we only execute the command after receiving all of its data) Array.Copy(request.Data, 0, transfer.Command.Data, offset, request.DataSegmentLength); if (offset + request.DataSegmentLength == totalLength) { // Last Data-out PDU connection.RemoveTransfer(request.TargetTransferTag); session.CommandsInTransfer.Remove(transfer.Command.CmdSN); if (session.IsPrecedingCommandPending(transfer.Command.CmdSN)) { session.DelayedCommands.Add(transfer.Command); } else { commandsToExecute.Add(transfer.Command); // Check if delayed commands are ready to be executed List <SCSICommandPDU> pendingCommands = session.GetDelayedCommandsReadyForExecution(); foreach (SCSICommandPDU pendingCommand in pendingCommands) { commandsToExecute.Add(pendingCommand); } } return(responseList); } else { // RFC 3720: An R2T MAY be answered with one or more SCSI Data-Out PDUs with a matching Target Transfer Tag. // If an R2T is answered with a single Data-Out PDU, the Buffer Offset in the Data PDU MUST be the same as the one specified // by the R2T, and the data length of the Data PDU MUST be the same as the Desired Data Transfer Length specified in the R2T. // If the R2T is answered with a sequence of Data PDUs, the Buffer Offset and Length MUST be within // the range of those specified by R2T, and the last PDU MUST have the F bit set to 1. // An R2T is considered outstanding until the last data PDU is transferred. if (request.Final) { // We already sent as many R2T as we could, we will only send R2T if any remained. if (transfer.NextR2TSN < transfer.TotalR2Ts) { // Send R2T ReadyToTransferPDU response = new ReadyToTransferPDU(); response.InitiatorTaskTag = request.InitiatorTaskTag; response.TargetTransferTag = request.TargetTransferTag; response.R2TSN = transfer.NextR2TSN; response.BufferOffset = transfer.NextOffset; // where we left off response.DesiredDataTransferLength = Math.Min((uint)connection.TargetMaxRecvDataSegmentLength, totalLength - response.BufferOffset); responseList.Add(response); transfer.NextR2TSN++; transfer.NextOffset += (uint)connection.TargetMaxRecvDataSegmentLength; } } return(responseList); } }