TargetResponseHelper.cs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  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. // Store segment (we only execute the command after receiving all of its data)
  27. byte[] commandData = new byte[command.ExpectedDataTransferLength];
  28. Array.Copy(command.Data, 0, commandData, 0, command.DataSegmentLength);
  29. connection.Transfers.Add(transferTag, new KeyValuePair<byte[], uint>(command.CommandDescriptorBlock, command.ExpectedDataTransferLength));
  30. connection.TransferData.Add(transferTag, commandData);
  31. // Send R2T
  32. ReadyToTransferPDU response = new ReadyToTransferPDU();
  33. response.InitiatorTaskTag = command.InitiatorTaskTag;
  34. response.R2TSN = 0; // R2Ts are sequenced per command and must start with 0 for each new command;
  35. response.TargetTransferTag = transferTag;
  36. response.BufferOffset = command.DataSegmentLength;
  37. response.DesiredDataTransferLength = Math.Min((uint)connection.TargetMaxRecvDataSegmentLength, command.ExpectedDataTransferLength - response.BufferOffset);
  38. // We store the next R2TSN to be used
  39. session.NextR2TSN.Add(transferTag, 1);
  40. responseList.Add(response);
  41. return responseList;
  42. }
  43. byte[] scsiResponse;
  44. SCSIStatusCodeName status = target.ExecuteCommand(command.CommandDescriptorBlock, command.LUN, command.Data, out scsiResponse);
  45. if (!command.Read || status != SCSIStatusCodeName.Good)
  46. {
  47. // RFC 3720: if the command is completed with an error, then the response and sense data MUST be sent in a SCSI Response PDU
  48. SCSIResponsePDU response = new SCSIResponsePDU();
  49. response.InitiatorTaskTag = command.InitiatorTaskTag;
  50. response.Status = status;
  51. response.Data = scsiResponse;
  52. if (command.Read)
  53. {
  54. EnforceExpectedDataTransferLength(response, command.ExpectedDataTransferLength);
  55. }
  56. responseList.Add(response);
  57. }
  58. else if (scsiResponse.Length <= connection.InitiatorMaxRecvDataSegmentLength)
  59. {
  60. SCSIDataInPDU response = new SCSIDataInPDU();
  61. response.InitiatorTaskTag = command.InitiatorTaskTag;
  62. response.Status = status;
  63. response.StatusPresent = true;
  64. response.Final = true;
  65. response.Data = scsiResponse;
  66. EnforceExpectedDataTransferLength(response, command.ExpectedDataTransferLength);
  67. responseList.Add(response);
  68. }
  69. else // we have to split the response to multiple Data-In PDUs
  70. {
  71. int bytesLeftToSend = scsiResponse.Length;
  72. uint dataSN = 0;
  73. while (bytesLeftToSend > 0)
  74. {
  75. int dataSegmentLength = Math.Min(connection.InitiatorMaxRecvDataSegmentLength, bytesLeftToSend);
  76. int dataOffset = scsiResponse.Length - bytesLeftToSend;
  77. SCSIDataInPDU response = new SCSIDataInPDU();
  78. response.InitiatorTaskTag = command.InitiatorTaskTag;
  79. if (bytesLeftToSend == dataSegmentLength)
  80. {
  81. // last Data-In PDU
  82. response.Status = status;
  83. response.StatusPresent = true;
  84. response.Final = true;
  85. }
  86. response.BufferOffset = (uint)dataOffset;
  87. response.DataSN = dataSN;
  88. dataSN++;
  89. response.Data = new byte[dataSegmentLength];
  90. Array.Copy(scsiResponse, dataOffset, response.Data, 0, dataSegmentLength);
  91. responseList.Add(response);
  92. bytesLeftToSend -= dataSegmentLength;
  93. }
  94. }
  95. return responseList;
  96. }
  97. internal static ISCSIPDU GetSCSIDataOutResponsePDU(SCSIDataOutPDU request, ISCSITarget target, SessionParameters session, ConnectionParameters connection)
  98. {
  99. string connectionIdentifier = StateObject.GetConnectionIdentifier(session, connection);
  100. if (connection.Transfers.ContainsKey(request.TargetTransferTag))
  101. {
  102. ushort LUN = (ushort)request.LUN;
  103. Disk disk = target.Disks[LUN];
  104. uint offset = request.BufferOffset;
  105. uint totalLength = connection.Transfers[request.TargetTransferTag].Value;
  106. // Store segment (we only execute the command after receiving all of its data)
  107. byte[] commandData = connection.TransferData[request.TargetTransferTag];
  108. Array.Copy(request.Data, 0, commandData, offset, request.DataSegmentLength);
  109. ISCSIServer.Log(String.Format("[{0}][GetSCSIDataOutResponsePDU] Buffer offset: {1}, Total length: {2}", connectionIdentifier, offset, totalLength));
  110. if (offset + request.DataSegmentLength == totalLength)
  111. {
  112. // Last Data-out PDU
  113. ISCSIServer.Log("[{0}][GetSCSIDataOutResponsePDU] Last Data-out PDU", connectionIdentifier);
  114. byte[] commandBytes = connection.Transfers[request.TargetTransferTag].Key;
  115. byte[] scsiResponse;
  116. SCSIStatusCodeName status = target.ExecuteCommand(commandBytes, request.LUN, commandData, out scsiResponse);
  117. SCSIResponsePDU response = new SCSIResponsePDU();
  118. response.InitiatorTaskTag = request.InitiatorTaskTag;
  119. response.Status = status;
  120. response.Data = scsiResponse;
  121. connection.Transfers.Remove(request.TargetTransferTag);
  122. connection.TransferData.Remove(request.TargetTransferTag);
  123. session.NextR2TSN.Remove(request.TargetTransferTag);
  124. return response;
  125. }
  126. else
  127. {
  128. // Send R2T
  129. ReadyToTransferPDU response = new ReadyToTransferPDU();
  130. response.InitiatorTaskTag = request.InitiatorTaskTag;
  131. response.TargetTransferTag = request.TargetTransferTag;
  132. response.R2TSN = session.GetNextR2TSN(request.TargetTransferTag);
  133. response.BufferOffset = offset + request.DataSegmentLength; // where we left off
  134. response.DesiredDataTransferLength = Math.Min((uint)connection.TargetMaxRecvDataSegmentLength, totalLength - response.BufferOffset);
  135. return response;
  136. }
  137. }
  138. else
  139. {
  140. ISCSIServer.Log("[{0}][GetSCSIDataOutResponsePDU] Invalid TargetTransferTag", connectionIdentifier);
  141. RejectPDU reject = new RejectPDU();
  142. reject.InitiatorTaskTag = request.InitiatorTaskTag;
  143. reject.Reason = RejectReason.InvalidPDUField;
  144. reject.Data = ByteReader.ReadBytes(request.GetBytes(), 0, 48);
  145. return reject;
  146. }
  147. }
  148. public static void EnforceExpectedDataTransferLength(SCSIResponsePDU response, uint expectedDataTransferLength)
  149. {
  150. if (response.Data.Length > expectedDataTransferLength)
  151. {
  152. response.ResidualOverflow = true;
  153. response.ResidualCount = (uint)(expectedDataTransferLength - response.Data.Length);
  154. response.Data = ByteReader.ReadBytes(response.Data, 0, (int)expectedDataTransferLength);
  155. }
  156. else if (response.Data.Length < expectedDataTransferLength)
  157. {
  158. response.ResidualUnderflow = true;
  159. response.ResidualCount = (uint)(expectedDataTransferLength - response.Data.Length);
  160. }
  161. }
  162. public static void EnforceExpectedDataTransferLength(SCSIDataInPDU response, uint expectedDataTransferLength)
  163. {
  164. if (response.Data.Length > expectedDataTransferLength)
  165. {
  166. response.ResidualOverflow = true;
  167. response.ResidualCount = (uint)(expectedDataTransferLength - response.Data.Length);
  168. response.Data = ByteReader.ReadBytes(response.Data, 0, (int)expectedDataTransferLength);
  169. }
  170. else if (response.Data.Length < expectedDataTransferLength)
  171. {
  172. response.ResidualUnderflow = true;
  173. response.ResidualCount = (uint)(expectedDataTransferLength - response.Data.Length);
  174. }
  175. }
  176. }
  177. }