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