public byte[] Read(ushort LUN, ulong sectorIndex, uint sectorCount, int bytesPerSector) { if (!m_isConnected) { throw new InvalidOperationException("iSCSI client is not connected"); } SCSICommandPDU readCommand = ClientHelper.GetRead16Command(m_session, m_connection, LUN, sectorIndex, sectorCount, bytesPerSector); SendPDU(readCommand); // RFC 3720: Data payload is associated with a specific SCSI command through the Initiator Task Tag SCSIDataInPDU data = WaitForPDU(readCommand.InitiatorTaskTag) as SCSIDataInPDU; byte[] result = new byte[sectorCount * bytesPerSector]; while (data != null) { Array.Copy(data.Data, 0, result, data.BufferOffset, data.DataSegmentLength); if (data.StatusPresent) { break; } data = WaitForPDU(readCommand.InitiatorTaskTag) as SCSIDataInPDU; } if (data != null && data.Status == SCSIStatusCodeName.Good) { return(result); } else { return(null); } }
/// <returns>Capacity in bytes</returns> public ulong ReadCapacity(ushort LUN, out int bytesPerSector) { if (!m_isConnected) { throw new InvalidOperationException("iSCSI client is not connected"); } SCSICommandPDU readCapacity = ClientHelper.GetReadCapacity10Command(m_session, m_connection, LUN); SendPDU(readCapacity); // SCSIResponsePDU with CheckCondition could be returned in case of an error SCSIDataInPDU data = WaitForPDU(readCapacity.InitiatorTaskTag) as SCSIDataInPDU; if (data != null && data.StatusPresent && data.Status == SCSIStatusCodeName.Good) { ReadCapacity10Parameter capacity = new ReadCapacity10Parameter(data.Data); if (capacity.ReturnedLBA != 0xFFFFFFFF) { bytesPerSector = (int)capacity.BlockLengthInBytes; return((capacity.ReturnedLBA + 1) * capacity.BlockLengthInBytes); } readCapacity = ClientHelper.GetReadCapacity16Command(m_session, m_connection, LUN); m_clientSocket.Send(readCapacity.GetBytes()); data = WaitForPDU(readCapacity.InitiatorTaskTag) as SCSIDataInPDU; if (data != null && data.StatusPresent && data.Status == SCSIStatusCodeName.Good) { ReadCapacity16Parameter capacity16 = new ReadCapacity16Parameter(data.Data); bytesPerSector = (int)capacity16.BlockLengthInBytes; return((capacity16.ReturnedLBA + 1) * capacity16.BlockLengthInBytes); } } bytesPerSector = 0; return(0); }
public static void EnforceExpectedDataTransferLength(SCSIDataInPDU response, uint expectedDataTransferLength) { if (response.Data.Length > expectedDataTransferLength) { response.ResidualOverflow = true; response.ResidualCount = (uint)(response.Data.Length - expectedDataTransferLength); response.Data = ByteReader.ReadBytes(response.Data, 0, (int)expectedDataTransferLength); } else if (response.Data.Length < expectedDataTransferLength) { response.ResidualUnderflow = true; response.ResidualCount = (uint)(expectedDataTransferLength - response.Data.Length); } }
public List <ushort> GetLUNList() { if (!m_isConnected) { throw new InvalidOperationException("iSCSI client is not connected"); } SCSICommandPDU reportLUNs = ClientHelper.GetReportLUNsCommand(m_session, m_connection, ReportLUNsParameter.MinimumAllocationLength); SendPDU(reportLUNs); SCSIDataInPDU data = WaitForPDU(reportLUNs.InitiatorTaskTag) as SCSIDataInPDU; if (data != null && data.StatusPresent && data.Status == SCSIStatusCodeName.Good) { uint requiredAllocationLength = ReportLUNsParameter.GetRequiredAllocationLength(data.Data); if (requiredAllocationLength > ReportLUNsParameter.MinimumAllocationLength) { reportLUNs = ClientHelper.GetReportLUNsCommand(m_session, m_connection, requiredAllocationLength); m_clientSocket.Send(reportLUNs.GetBytes()); data = WaitForPDU(reportLUNs.InitiatorTaskTag) as SCSIDataInPDU; if (data == null || !data.StatusPresent || data.Status != SCSIStatusCodeName.Good) { return(null); } } ReportLUNsParameter parameter = new ReportLUNsParameter(data.Data); List <ushort> result = new List <ushort>(); foreach (LUNStructure lun in parameter.LUNList) { if (lun.IsSingleLevelLUN) { result.Add(lun); } } return(result); } return(null); }
internal static List <ISCSIPDU> PrepareSCSICommandResponse(SCSICommandPDU command, SCSIStatusCodeName status, byte[] scsiResponse, ConnectionParameters connection) { List <ISCSIPDU> responseList = new List <ISCSIPDU>(); if (!command.Read || status != SCSIStatusCodeName.Good) { // RFC 3720: if the command is completed with an error, then the response and sense data MUST be sent in a SCSI Response PDU SCSIResponsePDU response = new SCSIResponsePDU(); response.InitiatorTaskTag = command.InitiatorTaskTag; response.Status = status; response.Data = scsiResponse; if (command.Read) { EnforceExpectedDataTransferLength(response, command.ExpectedDataTransferLength); } responseList.Add(response); } else if (scsiResponse.Length <= connection.InitiatorMaxRecvDataSegmentLength) { SCSIDataInPDU response = new SCSIDataInPDU(); response.InitiatorTaskTag = command.InitiatorTaskTag; response.Status = status; response.StatusPresent = true; response.Final = true; response.Data = scsiResponse; EnforceExpectedDataTransferLength(response, command.ExpectedDataTransferLength); responseList.Add(response); } else // we have to split the response to multiple Data-In PDUs { int bytesLeftToSend = scsiResponse.Length; uint dataSN = 0; while (bytesLeftToSend > 0) { int dataSegmentLength = Math.Min(connection.InitiatorMaxRecvDataSegmentLength, bytesLeftToSend); int dataOffset = scsiResponse.Length - bytesLeftToSend; SCSIDataInPDU response = new SCSIDataInPDU(); response.InitiatorTaskTag = command.InitiatorTaskTag; if (bytesLeftToSend == dataSegmentLength) { // last Data-In PDU response.Status = status; response.StatusPresent = true; response.Final = true; } response.BufferOffset = (uint)dataOffset; response.DataSN = dataSN; dataSN++; response.Data = new byte[dataSegmentLength]; Array.Copy(scsiResponse, dataOffset, response.Data, 0, dataSegmentLength); responseList.Add(response); bytesLeftToSend -= dataSegmentLength; } } return(responseList); }
internal static List <ISCSIPDU> GetSCSIResponsePDU(SCSICommandPDU command, ISCSITarget target, SessionParameters session, ConnectionParameters connection) { // We return either SCSIResponsePDU or List<SCSIDataInPDU> List <ISCSIPDU> responseList = new List <ISCSIPDU>(); string connectionIdentifier = StateObject.GetConnectionIdentifier(session, connection); if (command.Write && command.DataSegmentLength < command.ExpectedDataTransferLength) { uint transferTag = session.GetNextTransferTag(); // Store segment (we only execute the command after receiving all of its data) byte[] commandDataBuffer = new byte[command.ExpectedDataTransferLength]; Array.Copy(command.Data, 0, commandDataBuffer, 0, command.DataSegmentLength); // Send R2T ReadyToTransferPDU response = new ReadyToTransferPDU(); response.InitiatorTaskTag = command.InitiatorTaskTag; response.R2TSN = 0; // R2Ts are sequenced per command and must start with 0 for each new command; response.TargetTransferTag = transferTag; response.BufferOffset = command.DataSegmentLength; response.DesiredDataTransferLength = Math.Min((uint)connection.TargetMaxRecvDataSegmentLength, command.ExpectedDataTransferLength - response.BufferOffset); connection.AddTransfer(transferTag, command.CommandDescriptorBlock, commandDataBuffer, 1); responseList.Add(response); return(responseList); } byte[] scsiResponse; SCSIStatusCodeName status = target.ExecuteCommand(command.CommandDescriptorBlock, command.LUN, command.Data, out scsiResponse); if (!command.Read || status != SCSIStatusCodeName.Good) { // RFC 3720: if the command is completed with an error, then the response and sense data MUST be sent in a SCSI Response PDU SCSIResponsePDU response = new SCSIResponsePDU(); response.InitiatorTaskTag = command.InitiatorTaskTag; response.Status = status; response.Data = scsiResponse; if (command.Read) { EnforceExpectedDataTransferLength(response, command.ExpectedDataTransferLength); } responseList.Add(response); } else if (scsiResponse.Length <= connection.InitiatorMaxRecvDataSegmentLength) { SCSIDataInPDU response = new SCSIDataInPDU(); response.InitiatorTaskTag = command.InitiatorTaskTag; response.Status = status; response.StatusPresent = true; response.Final = true; response.Data = scsiResponse; EnforceExpectedDataTransferLength(response, command.ExpectedDataTransferLength); responseList.Add(response); } else // we have to split the response to multiple Data-In PDUs { int bytesLeftToSend = scsiResponse.Length; uint dataSN = 0; while (bytesLeftToSend > 0) { int dataSegmentLength = Math.Min(connection.InitiatorMaxRecvDataSegmentLength, bytesLeftToSend); int dataOffset = scsiResponse.Length - bytesLeftToSend; SCSIDataInPDU response = new SCSIDataInPDU(); response.InitiatorTaskTag = command.InitiatorTaskTag; if (bytesLeftToSend == dataSegmentLength) { // last Data-In PDU response.Status = status; response.StatusPresent = true; response.Final = true; } response.BufferOffset = (uint)dataOffset; response.DataSN = dataSN; dataSN++; response.Data = new byte[dataSegmentLength]; Array.Copy(scsiResponse, dataOffset, response.Data, 0, dataSegmentLength); responseList.Add(response); bytesLeftToSend -= dataSegmentLength; } } return(responseList); }