TargetResponseHelper.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. /* Copyright (C) 2012-2016 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
  2. *
  3. * You can redistribute this program and/or modify it under the terms of
  4. * the GNU Lesser Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. */
  7. using System;
  8. using System.Collections.Generic;
  9. using System.IO;
  10. using System.Text;
  11. using System.Runtime.InteropServices;
  12. using DiskAccessLibrary;
  13. using Utilities;
  14. namespace ISCSI.Server
  15. {
  16. public class TargetResponseHelper
  17. {
  18. internal static List<ISCSIPDU> GetSCSIResponsePDU(SCSICommandPDU command, ISCSITarget target, SessionParameters session, ConnectionParameters connection)
  19. {
  20. // We return either SCSIResponsePDU or List<SCSIDataInPDU>
  21. List<ISCSIPDU> responseList = new List<ISCSIPDU>();
  22. string connectionIdentifier = StateObject.GetConnectionIdentifier(session, connection);
  23. if (command.Write && command.DataSegmentLength < command.ExpectedDataTransferLength)
  24. {
  25. uint transferTag = session.GetNextTransferTag();
  26. // Create buffer for next segments (we only execute the command after receiving all of its data)
  27. Array.Resize<byte>(ref command.Data, (int)command.ExpectedDataTransferLength);
  28. // Send R2T
  29. ReadyToTransferPDU response = new ReadyToTransferPDU();
  30. response.InitiatorTaskTag = command.InitiatorTaskTag;
  31. response.R2TSN = 0; // R2Ts are sequenced per command and must start with 0 for each new command;
  32. response.TargetTransferTag = transferTag;
  33. response.BufferOffset = command.DataSegmentLength;
  34. response.DesiredDataTransferLength = Math.Min((uint)connection.TargetMaxRecvDataSegmentLength, command.ExpectedDataTransferLength - response.BufferOffset);
  35. connection.AddTransfer(transferTag, command, 1);
  36. responseList.Add(response);
  37. return responseList;
  38. }
  39. ISCSIServer.Log("[{0}] Executing Command: CmdSN: {1}", connectionIdentifier, command.CmdSN);
  40. byte[] scsiResponse;
  41. SCSIStatusCodeName status = target.ExecuteCommand(command.CommandDescriptorBlock, command.LUN, command.Data, out scsiResponse);
  42. if (!command.Read || status != SCSIStatusCodeName.Good)
  43. {
  44. // RFC 3720: if the command is completed with an error, then the response and sense data MUST be sent in a SCSI Response PDU
  45. SCSIResponsePDU response = new SCSIResponsePDU();
  46. response.InitiatorTaskTag = command.InitiatorTaskTag;
  47. response.Status = status;
  48. response.Data = scsiResponse;
  49. if (command.Read)
  50. {
  51. EnforceExpectedDataTransferLength(response, command.ExpectedDataTransferLength);
  52. }
  53. responseList.Add(response);
  54. }
  55. else if (scsiResponse.Length <= connection.InitiatorMaxRecvDataSegmentLength)
  56. {
  57. SCSIDataInPDU response = new SCSIDataInPDU();
  58. response.InitiatorTaskTag = command.InitiatorTaskTag;
  59. response.Status = status;
  60. response.StatusPresent = true;
  61. response.Final = true;
  62. response.Data = scsiResponse;
  63. EnforceExpectedDataTransferLength(response, command.ExpectedDataTransferLength);
  64. responseList.Add(response);
  65. }
  66. else // we have to split the response to multiple Data-In PDUs
  67. {
  68. int bytesLeftToSend = scsiResponse.Length;
  69. uint dataSN = 0;
  70. while (bytesLeftToSend > 0)
  71. {
  72. int dataSegmentLength = Math.Min(connection.InitiatorMaxRecvDataSegmentLength, bytesLeftToSend);
  73. int dataOffset = scsiResponse.Length - bytesLeftToSend;
  74. SCSIDataInPDU response = new SCSIDataInPDU();
  75. response.InitiatorTaskTag = command.InitiatorTaskTag;
  76. if (bytesLeftToSend == dataSegmentLength)
  77. {
  78. // last Data-In PDU
  79. response.Status = status;
  80. response.StatusPresent = true;
  81. response.Final = true;
  82. }
  83. response.BufferOffset = (uint)dataOffset;
  84. response.DataSN = dataSN;
  85. dataSN++;
  86. response.Data = new byte[dataSegmentLength];
  87. Array.Copy(scsiResponse, dataOffset, response.Data, 0, dataSegmentLength);
  88. responseList.Add(response);
  89. bytesLeftToSend -= dataSegmentLength;
  90. }
  91. }
  92. return responseList;
  93. }
  94. internal static ISCSIPDU GetSCSIDataOutResponsePDU(SCSIDataOutPDU request, ISCSITarget target, SessionParameters session, ConnectionParameters connection)
  95. {
  96. string connectionIdentifier = StateObject.GetConnectionIdentifier(session, connection);
  97. TransferEntry transfer = connection.GetTransferEntry(request.TargetTransferTag);
  98. if (transfer == null)
  99. {
  100. ISCSIServer.Log("[{0}][GetSCSIDataOutResponsePDU] Invalid TargetTransferTag {1}", connectionIdentifier, request.TargetTransferTag);
  101. RejectPDU reject = new RejectPDU();
  102. reject.InitiatorTaskTag = request.InitiatorTaskTag;
  103. reject.Reason = RejectReason.InvalidPDUField;
  104. reject.Data = ByteReader.ReadBytes(request.GetBytes(), 0, 48);
  105. return reject;
  106. }
  107. ushort LUN = (ushort)request.LUN;
  108. Disk disk = target.Disks[LUN];
  109. uint offset = request.BufferOffset;
  110. uint totalLength = (uint)transfer.Command.ExpectedDataTransferLength;
  111. // Store segment (we only execute the command after receiving all of its data)
  112. Array.Copy(request.Data, 0, transfer.Command.Data, offset, request.DataSegmentLength);
  113. ISCSIServer.Log(String.Format("[{0}][GetSCSIDataOutResponsePDU] Buffer offset: {1}, Total length: {2}", connectionIdentifier, offset, totalLength));
  114. if (offset + request.DataSegmentLength == totalLength)
  115. {
  116. // Last Data-out PDU
  117. ISCSIServer.Log("[{0}] Executing Command: CmdSN: {1}", connectionIdentifier, transfer.Command.CmdSN);
  118. byte[] scsiResponse;
  119. SCSIStatusCodeName status = target.ExecuteCommand(transfer.Command.CommandDescriptorBlock, transfer.Command.LUN, transfer.Command.Data, out scsiResponse);
  120. SCSIResponsePDU response = new SCSIResponsePDU();
  121. response.InitiatorTaskTag = request.InitiatorTaskTag;
  122. response.Status = status;
  123. response.Data = scsiResponse;
  124. connection.RemoveTransfer(request.TargetTransferTag);
  125. return response;
  126. }
  127. else
  128. {
  129. // Send R2T
  130. ReadyToTransferPDU response = new ReadyToTransferPDU();
  131. response.InitiatorTaskTag = request.InitiatorTaskTag;
  132. response.TargetTransferTag = request.TargetTransferTag;
  133. response.R2TSN = transfer.NextR2NSN;
  134. response.BufferOffset = offset + request.DataSegmentLength; // where we left off
  135. response.DesiredDataTransferLength = Math.Min((uint)connection.TargetMaxRecvDataSegmentLength, totalLength - response.BufferOffset);
  136. transfer.NextR2NSN++;
  137. return response;
  138. }
  139. }
  140. public static void EnforceExpectedDataTransferLength(SCSIResponsePDU response, uint expectedDataTransferLength)
  141. {
  142. if (response.Data.Length > expectedDataTransferLength)
  143. {
  144. response.ResidualOverflow = true;
  145. response.ResidualCount = (uint)(response.Data.Length - expectedDataTransferLength);
  146. response.Data = ByteReader.ReadBytes(response.Data, 0, (int)expectedDataTransferLength);
  147. }
  148. else if (response.Data.Length < expectedDataTransferLength)
  149. {
  150. response.ResidualUnderflow = true;
  151. response.ResidualCount = (uint)(expectedDataTransferLength - response.Data.Length);
  152. }
  153. }
  154. public static void EnforceExpectedDataTransferLength(SCSIDataInPDU response, uint expectedDataTransferLength)
  155. {
  156. if (response.Data.Length > expectedDataTransferLength)
  157. {
  158. response.ResidualOverflow = true;
  159. response.ResidualCount = (uint)(response.Data.Length - expectedDataTransferLength);
  160. response.Data = ByteReader.ReadBytes(response.Data, 0, (int)expectedDataTransferLength);
  161. }
  162. else if (response.Data.Length < expectedDataTransferLength)
  163. {
  164. response.ResidualUnderflow = true;
  165. response.ResidualCount = (uint)(expectedDataTransferLength - response.Data.Length);
  166. }
  167. }
  168. }
  169. }