TargetResponseHelper.cs 8.7 KB

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