package org.jscsi.target.connection.stage.fullfeature;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.jscsi.exception.InternetSCSIException;
import org.jscsi.parser.ProtocolDataUnit;
import org.jscsi.parser.scsi.SCSIResponseParser;
import org.jscsi.parser.scsi.SCSIResponseParser.ServiceResponse;
import org.jscsi.parser.scsi.SCSIStatus;
import org.jscsi.target.connection.TargetPduFactory;
import org.jscsi.target.connection.phase.TargetFullFeaturePhase;
import org.jscsi.target.connection.stage.TargetStage;
import org.jscsi.target.scsi.IResponseData;
import org.jscsi.target.scsi.ScsiResponseDataSegment;
import org.jscsi.target.scsi.sense.AdditionalSenseBytes;
import org.jscsi.target.scsi.sense.AdditionalSenseCodeAndQualifier;
import org.jscsi.target.scsi.sense.ErrorType;
import org.jscsi.target.scsi.sense.FixedFormatSenseData;
import org.jscsi.target.scsi.sense.SenseKey;
import org.jscsi.target.scsi.sense.information.FourByteInformation;
import org.jscsi.target.scsi.sense.senseDataDescriptor.senseKeySpecific.FieldPointerSenseKeySpecificData;
import org.jscsi.utils.ByteBufferCache;
/**
* This class is an abstract super-class for stages of the {@link TargetFullFeaturePhase}.
*
* @see TargetStage
* @author Andreas Ergenzinger
*/
public abstract class TargetFullFeatureStage extends TargetStage {
/**
* The abstract constructor.
*
* @param targetFullFeaturePhase
* the phase this stage is a part of
*/
public TargetFullFeatureStage(TargetFullFeaturePhase targetFullFeaturePhase) {
super(targetFullFeaturePhase);
}
/**
* Creates a PDU with {@link FixedFormatSenseData} that must be sent to the
* initiator after receiving a Command Descriptor Block with an illegal
* field.
*
* @param senseKeySpecificData
* contains a list of all illegal fields
* @param additionalSenseCodeAndQualifier
* provides more specific information about the cause of the
* check condition
* @param initiatorTaskTag
* used by the initiator to identify the task
* @param expectedDataTransferLength
* the amount of payload data expected by the initiator (i.e.
* allocated buffer space)
* @return the error PDU
*/
protected static final ProtocolDataUnit createFixedFormatErrorPdu(
final FieldPointerSenseKeySpecificData[] senseKeySpecificData,
final AdditionalSenseCodeAndQualifier additionalSenseCodeAndQualifier, final int initiatorTaskTag,
final int expectedDataTransferLength) {
// create the whole sense data
FixedFormatSenseData senseData = new FixedFormatSenseData(false,// valid
ErrorType.CURRENT,// error type
false,// file mark
false,// end of medium
false,// incorrect length indicator
SenseKey.ILLEGAL_REQUEST,// sense key
new FourByteInformation(),// information
new FourByteInformation(),// command specific information
additionalSenseCodeAndQualifier,// additional sense code and
// qualifier
(byte)0,// field replaceable unit code
senseKeySpecificData[0],// sense key specific data, only report
// first problem
new AdditionalSenseBytes());// additional sense bytes
// keep only the part of the sense data that will be sent
final ScsiResponseDataSegment dataSegment =
new ScsiResponseDataSegment(senseData, expectedDataTransferLength);
final int senseDataSize = senseData.size();
// calculate residuals and flags
final int residualCount = Math.abs(expectedDataTransferLength - senseDataSize);
final boolean residualOverflow = expectedDataTransferLength < senseDataSize;
final boolean residualUnderflow = expectedDataTransferLength > senseDataSize;
// create and return PDU
return TargetPduFactory.createSCSIResponsePdu(false,// bidirectionalReadResidualOverflow
false,// bidirectionalReadResidualUnderflow
residualOverflow,// residualOverflow
residualUnderflow,// residualUnderflow,
SCSIResponseParser.ServiceResponse.COMMAND_COMPLETED_AT_TARGET,// response,
SCSIStatus.CHECK_CONDITION,// status,
initiatorTaskTag,// initiatorTaskTag,
0,// snackTag
0,// expectedDataSequenceNumber
0,// bidirectionalReadResidualCount
residualCount,// residualCount
dataSegment);// data segment
}
/**
* Creates a PDU with {@link FixedFormatSenseData} that must be sent to the
* initiator after receiving a Command Descriptor Block with an illegal
* field, which requires the the additional sense code
* {@link AdditionalSenseCodeAndQualifier#INVALID_FIELD_IN_CDB}.
*
* @param senseKeySpecificData
* contains a list of all illegal fields
* @param initiatorTaskTag
* used by the initiator to identify the task
* @param expectedDataTransferLength
* the amount of payload data expected by the initiator (i.e.
* allocated buffer space)
* @return the error PDU
*/
protected static final ProtocolDataUnit createFixedFormatErrorPdu(
final FieldPointerSenseKeySpecificData[] senseKeySpecificData, final int initiatorTaskTag,
final int expectedDataTransferLength) {
return createFixedFormatErrorPdu(senseKeySpecificData,
AdditionalSenseCodeAndQualifier.INVALID_FIELD_IN_CDB, initiatorTaskTag,
expectedDataTransferLength);
}
/**
* Creates a SCSI Response PDU with a length zero data segment. Objects
* created with this method can be used as replies in task terminating with {@link SCSIStatus#GOOD} which
* do not require additional data to be
* transfered, or for creating follow-up PDU with {@link SCSIStatus#CHECK_CONDITION} status sent after
* Data-In PDUs with
* sense data.
*
* @param status
* the SCSI status of the task
* @param initiatorTaskTag
* used by the initiator to identify the task
* @param expectedDataTransferLength
* total amount of payload data in bytes expected by the
* initiator
* @param responseDataSize
* actual amount of payload data in bytes sent by the target
* @return the SCSI Response PDU
*/
protected static final ProtocolDataUnit createScsiResponsePdu(final SCSIStatus status,
final int initiatorTaskTag, final int expectedDataTransferLength, final int responseDataSize) {
// calculate residuals and flags
final int residualCount = Math.abs(expectedDataTransferLength - responseDataSize);
final boolean residualOverflow = expectedDataTransferLength < responseDataSize;
final boolean residualUnderflow = expectedDataTransferLength > responseDataSize;
return TargetPduFactory.createSCSIResponsePdu(false,// bidirectionalReadResidualOverflow
false,// bidirectionalReadResidualUnderflow
residualOverflow,// residualOverflow,
residualUnderflow,// residualUnderflow,
SCSIResponseParser.ServiceResponse.COMMAND_COMPLETED_AT_TARGET,// response
status,// status
initiatorTaskTag,// initiatorTaskTag
0,// snackTag
0,// expectedDataSequenceNumber
0,// bidirectionalReadResidualCount
residualCount,// residualCount
ScsiResponseDataSegment.EMPTY_DATA_SEGMENT);// data segment
}
/**
* Sends a two byte sequence of a Data-In and a SCSI Response PDU with the
* specified <i>responseData</i> payload to the initiator.
*
* @param initiatorTaskTag
* used by the initiator to identify the task
* @param expectedDataTransferLength
* the total amount of payload data in bytes expected by the
* initiator
* <p>
* The method might throw exceptions during PDU serialization and sending.
* @param responseData
* the data requested by the initiator
* @throws InterruptedException
* @throws IOException
* @throws InternetSCSIException
*/
protected final void sendResponse(final int initiatorTaskTag, final int expectedDataTransferLength,
final IResponseData responseData) throws InterruptedException, IOException, InternetSCSIException {
// serialize all response data
final int responseDataLength = responseData.size();
ByteBuffer trimmedBuffer = null;
final ByteBuffer fullBuffer = ByteBufferCache.allocate(responseDataLength);
try{
responseData.serialize(fullBuffer, 0);
fullBuffer.rewind();
// copy fullBuffer to buffer with size trimmed to
// expectedDataTransferLength
if (fullBuffer.capacity() <= expectedDataTransferLength) {
// no trimming
trimmedBuffer = fullBuffer;
} else {
trimmedBuffer = ByteBufferCache.allocate(expectedDataTransferLength);
fullBuffer.rewind().limit(expectedDataTransferLength);
trimmedBuffer.put(fullBuffer);
/*
trimmedBuffer.put(fullBuffer.array(),// source array
0,// offset in source
expectedDataTransferLength);// length
*/
}
// coompute residual count and associated flags
final boolean residualOverflow = expectedDataTransferLength < responseDataLength;
final boolean residualUnderflow = expectedDataTransferLength > responseDataLength;
final int residualCount = Math.abs(expectedDataTransferLength - responseDataLength);
// //create and send PDU//TODO this worked for Open-iSCSI, MS Initiator
// does not like phase collapse
// ProtocolDataUnit pdu = TargetPduFactory.createDataInPdu(
// true,//finalFlag
// false,//acknowledgeFlag always false
// residualOverflow,//residualOverflowFlag
// residualUnderflow,//residualUnderflowFlag
// true,//statusFlag
// SCSIStatus.GOOD,//status
// 0,//logicalUnitNumber reserved
// initiatorTaskTag,//initiatorTaskTag
// -1,//targetTransferTag reserved
// 0,//dataSequenceNumber
// 0,//bufferOffset
// residualCount,//residualCount
// trimmedBuffer);//dataSegment
// create and send PDU
ProtocolDataUnit pdu = TargetPduFactory.createDataInPdu(true,// finalFlag
false,// acknowledgeFlag always false
false,// residualOverflowFlag x
false,// residualUnderflowFlag x
false,// statusFlag
SCSIStatus.GOOD,// status, reserved
0,// logicalUnitNumber reserved
initiatorTaskTag,// initiatorTaskTag
-1,// targetTransferTag reserved
0,// dataSequenceNumber
0,// bufferOffset
0,// residualCount x
trimmedBuffer);// dataSegment
connection.sendPdu(pdu);
pdu = TargetPduFactory.createSCSIResponsePdu(false,// bidirectionalReadResidualOverflow
false,// bidirectionalReadResidualUnderflow
residualOverflow,// residualOverflow
residualUnderflow,// residualUnderflow
ServiceResponse.COMMAND_COMPLETED_AT_TARGET,// response
SCSIStatus.GOOD,// status
initiatorTaskTag, 0,// snackTag, reserved
0,// expectedDataSequenceNumber
0,// bidirectionalReadResidualCount
residualCount,// residualCount
ScsiResponseDataSegment.EMPTY_DATA_SEGMENT);// scsiResponseDataSegment
connection.sendPdu(pdu);
} finally { if (fullBuffer != trimmedBuffer) ByteBufferCache.release(fullBuffer);}
}
}