
org.jscsi.parser.scsi.SCSIResponseParser Maven / Gradle / Ivy
/**
* Copyright (c) 2012, University of Konstanz, Distributed Systems Group All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or other materials provided with the
* distribution. * Neither the name of the University of Konstanz nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jscsi.parser.scsi;
import java.util.HashMap;
import java.util.Map;
import org.jscsi.exception.InternetSCSIException;
import org.jscsi.parser.Constants;
import org.jscsi.parser.ProtocolDataUnit;
import org.jscsi.parser.TargetMessageParser;
import org.jscsi.parser.datasegment.DataSegmentFactory.DataSegmentFormat;
import org.jscsi.utils.Utils;
/**
* SCSIResponseParser
*
* This class parses a SCSI Response message defined in the iSCSI Standard (RFC3720).
*
*
StatSN - Status Sequence Number
StatSN is a Sequence Number that the target iSCSI layer generates per
* connection and that in turn, enables the initiator to acknowledge status reception. StatSN is incremented by
* 1
for every response/status sent on a connection except for responses sent as a result of a retry or
* SNACK. In the case of responses sent due to a retransmission request, the StatSN MUST be the same as the first time
* the PDU was sent unless the connection has since been restarted.
*
*
ExpCmdSN - Next Expected CmdSN from this Initiator
ExpCmdSN is a Sequence Number that the target iSCSI
* returns to the initiator to acknowledge command reception. It is used to update a local variable with the same name.
* An ExpCmdSN equal to MaxCmdSN + 1
indicates that the target cannot accept new commands.
*
*
MaxCmdSN - Maximum CmdSN from this Initiator
MaxCmdSN is a Sequence Number that the target iSCSI returns to
* the initiator to indicate the maximum CmdSN the initiator can send. It is used to update a local variable with the
* same name. If MaxCmdSN is equal to ExpCmdSN - 1
, this indicates to the initiator that the target cannot
* receive any additional commands. When MaxCmdSN changes at the target while the target has no pending PDUs to convey
* this information to the initiator, it MUST generate a NOP-IN to carry the new MaxCmdSN.
*
* iSCSI targets MUST support and enable autosense. If Status is CHECK CONDITION (0x02
), then the Data
* Segment MUST contain sense data for the failed command.
* For some iSCSI responses, the response data segment MAY contain some response related information, (e.g., for a
* target failure, it may contain a vendor specific detailed description of the failure).
*
* @author Volker Wildi, University of Konstanz
*/
public class SCSIResponseParser extends TargetMessageParser {
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* This enumerations defines all valid service responses, which are defined in the iSCSI Standard (RFC 3720).
*
* 0x80-0xff - Vendor specific
*/
public static enum ServiceResponse {
/** Command completed at target. */
COMMAND_COMPLETED_AT_TARGET((byte) 0x00),
/** Target Failure. */
TARGET_FAILURE((byte) 0x01);
private final byte value;
private static Map mapping;
static {
ServiceResponse.mapping = new HashMap();
for (ServiceResponse s : values()) {
ServiceResponse.mapping.put(s.value, s);
}
}
private ServiceResponse (final byte newValue) {
value = newValue;
}
/**
* Returns the value of this enumeration.
*
* @return The value of this enumeration.
*/
public final byte value () {
return value;
}
/**
* Returns the constant defined for the given value
.
*
* @param value The value to search for.
* @return The constant defined for the given value
. Or null
, if this value is not
* defined by this enumeration.
*/
public static final ServiceResponse valueOf (final byte value) {
return ServiceResponse.mapping.get(value);
}
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** Bit mask for the bits 1,2, and 7
. These bits are reserved. */
private static final int RESERVED_FLAGS_MASK = 0x610000;
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** The o-bit. */
private boolean bidirectionalReadResidualOverflow;
/** The u-bit. */
private boolean bidirectionalReadResidualUnderflow;
/** The O-bit. */
private boolean residualOverflow;
/** The U-bit. */
private boolean residualUnderflow;
private ServiceResponse response;
/** The Status code. */
private SCSIStatus status;
/** The SNACK Tag. */
private int snackTag;
/** The Expected Data Sequence Number. */
private int expectedDataSequenceNumber;
private int bidirectionalReadResidualCount;
private int residualCount;
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* Default constructor, creates a new, empty SCSIResponseParser
object.
*
* @param initProtocolDataUnit The reference ProtocolDataUnit
instance, which contains this
* SCSIResponseParser
subclass object.
*/
public SCSIResponseParser (final ProtocolDataUnit initProtocolDataUnit) {
super(initProtocolDataUnit);
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
public final String toString () {
final StringBuilder sb = new StringBuilder(Constants.LOG_INITIAL_SIZE);
Utils.printField(sb, "Response", response.value(), 1);
Utils.printField(sb, "SNACK TAG", snackTag, 1);
sb.append(super.toString());
Utils.printField(sb, "ExpDataSN", expectedDataSequenceNumber, 1);
Utils.printField(sb, "BidirectionalReadResidualOverflow", bidirectionalReadResidualOverflow, 1);
Utils.printField(sb, "BidirectionalReadResidualUnderflow", bidirectionalReadResidualUnderflow, 1);
Utils.printField(sb, "ResidualOverflow", residualOverflow, 1);
Utils.printField(sb, "ResidualUnderflow", residualUnderflow, 1);
Utils.printField(sb, "ResidualCount", residualCount, 1);
Utils.printField(sb, "Bidirectional Read Residual Count", bidirectionalReadResidualCount, 1);
return sb.toString();
}
/** {@inheritDoc} */
@Override
public final DataSegmentFormat getDataSegmentFormat () {
return DataSegmentFormat.SCSI_RESPONSE;
}
/** {@inheritDoc} */
@Override
public final void clear () {
super.clear();
bidirectionalReadResidualOverflow = false;
bidirectionalReadResidualUnderflow = false;
residualOverflow = false;
residualUnderflow = false;
response = null;
status = null;
snackTag = 0x00000000;
bidirectionalReadResidualCount = 0x00000000;
residualCount = 0x00000000;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* The Bidirectional Read Residual Count field MUST be valid in the case where either the u bit or the o bit is set.
* If neither bit is set, the Bidirectional Read Residual Count field is reserved. Targets may set the Bidirectional
* Read Residual Count and initiators may use it when the response code is "completed at target". If the o bit is
* set, the Bidirectional Read Residual Count indicates the number of bytes that were not transferred to the
* initiator because the initiator’s Expected Bidirectional Read Transfer Length was not sufficient. If the u bit is
* set, the Bidirectional Read Residual Count indicates the number of bytes that were not transferred to the
* initiator out of the number of bytes expected to be transferred.
*
* @return The bidirectional read residual count of this SCSIResponseParser
object.
*/
public final int getBidirectionalReadResidualCount () {
return bidirectionalReadResidualCount;
}
/**
* The number of R2T and Data-In (read) PDUs the target has sent for the command.
*
* This field MUST be 0
if the response code is not Command Completed at Target or the target sent no
* Data-In PDUs for the command.
*
* @return The expected data sequence number of this SCSIResponseParser
object.
*/
public final int getExpectedDataSequenceNumber () {
return expectedDataSequenceNumber;
}
/**
* Returns the status of the Bidirectional Read Residual Overflow flag. In this case, the Bidirectional Read
* Residual Count indicates the number of bytes that were not transferred to the initiator because the initiator’s
* Expected Bidirectional Read Data Transfer Length was not sufficient.
*
* @return true
, if it is set; else false
.
*/
public final boolean isBidirectionalReadResidualOverflow () {
return bidirectionalReadResidualOverflow;
}
/**
* Returns the status of the Bidirectional Read Residual Underflow flag. In this case, the Bidirectional Read
* Residual Count indicates the number of bytes that were not transferred to the initiator out of the number of
* bytes expected to be transferred.
*
* @return true
, if it is set; else false
.
*/
public final boolean isBidirectionalReadResidualUnderflow () {
return bidirectionalReadResidualUnderflow;
}
/**
* The Residual Count field MUST be valid in the case where either the U bit or the O bit is set. If neither bit is
* set, the Residual Count field is reserved. Targets may set the residual count and initiators may use it when the
* response code is "completed at target" (even if the status returned is not GOOD). If the O bit is set, the
* Residual Count indicates the number of bytes that were not transferred because the initiator’s Expected Data
* Transfer Length was not sufficient. If the U bit is set, the Residual Count indicates the number of bytes that
* were not transferred out of the number of bytes expected to be transferred.
*
* @return The residual count of this SCSIResponseParser
object.
*/
public final int getResidualCount () {
return residualCount;
}
/**
* Returns the status of the Residual Overflow flag. In this case, the Residual Count indicates the number of bytes
* that were not transferred because the initiator’s Expected Data Transfer Length was not sufficient. For a
* bidirectional operation, the Residual Count contains the residual for the write operation.
*
* @return true
, if it is set; else false
.
*/
public final boolean isResidualOverflow () {
return residualOverflow;
}
/**
* Returns the status of the Residual Underflow flag. In this case, the Residual Count indicates the number of bytes
* that were not transferred out of the number of bytes that were expected to be transferred. For a bidirectional
* operation, the Residual Count contains the residual for the write operation.
*
* @return true
, if it is set; else false
.
*/
public final boolean isResidualUnderflow () {
return residualUnderflow;
}
/**
* This field contains the iSCSI service response.
*
* All other response codes are reserved.
*
* The Response is used to report a Service Response. The mapping of the response code into a SCSI service response
* code value, if needed, is outside the scope of this document. However, in symbolic terms response value 0x00 maps
* to the SCSI service response (see [SAM2] and [SPC3]) of TASK COMPLETE or LINKED COMMAND COMPLETE. All other
* Response values map to the SCSI service response of SERVICE DELIVERY OR TARGET FAILURE.
*
* If a PDU that includes SCSI status (Response PDU or Data-In PDU including status) does not arrive before the
* session is terminated, the SCSI service response is SERVICE DELIVERY OR TARGET FAILURE. A non-zero Response field
* indicates a failure to execute the command in which case the Status and Flag fields are undefined.
*
* @return The service response of this SCSIResponseParser
object.
* @see ServiceResponse
*/
public final ServiceResponse getResponse () {
return response;
}
/**
* This field contains a copy of the SNACK Tag of the last SNACK Tag accepted by the target on the same connection
* and for the command for which the response is issued. Otherwise it is reserved and should be set to
* 0
.
*
* After issuing a R-Data SNACK the initiator must discard any SCSI status unless contained in an SCSI Response PDU
* carrying the same SNACK Tag as the last issued R-Data SNACK for the SCSI command on the current connection.
*
* For a detailed discussion on R-Data SNACK see Section 10.16 SNACK Request.
*
* @return The SNACK Tag of this SCSIResponseParser
object.
*/
public final int getSNACKTag () {
return snackTag;
}
/**
* The Status field is used to report the SCSI status of the command (as specified in [SAM2]) and is only valid if
* the Response Code is Command Completed at target.
*
* @return The status field of this SCSIResponseParser
object.
* @see org.jscsi.parser.scsi.SCSIStatus
*/
public final SCSIStatus getStatus () {
return status;
}
public final void setBidirectionalReadResidualCount (int bidirectionalReadResidualCount) {
this.bidirectionalReadResidualCount = bidirectionalReadResidualCount;
}
public final void setBidirectionalReadResidualOverflow (boolean bidirectionalReadResidualOverflow) {
this.bidirectionalReadResidualOverflow = bidirectionalReadResidualOverflow;
}
public final void setBidirectionalReadResidualUnderflow (boolean bidirectionalReadResidualUnderflow) {
this.bidirectionalReadResidualUnderflow = bidirectionalReadResidualUnderflow;
}
public final void setExpectedDataSequenceNumber (int expectedDataSequenceNumber) {
this.expectedDataSequenceNumber = expectedDataSequenceNumber;
}
public final void setResidualCount (int residualCount) {
this.residualCount = residualCount;
}
public final void setResidualOverflow (boolean residualOverflow) {
this.residualOverflow = residualOverflow;
}
public final void setResidualUnderflow (boolean residualUnderflow) {
this.residualUnderflow = residualUnderflow;
}
public void setResponse (SCSIResponseParser.ServiceResponse response) {
this.response = response;
}
public final void setSNACKTag (int snackTag) {
this.snackTag = snackTag;
}
public final void setStatus (SCSIStatus status) {
this.status = status;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
protected final void deserializeBytes1to3 (final int line) throws InternetSCSIException {
Utils.isReserved(line & RESERVED_FLAGS_MASK);
bidirectionalReadResidualOverflow = Utils.isBitSet(line & Constants.READ_RESIDUAL_OVERFLOW_FLAG_MASK);
bidirectionalReadResidualUnderflow = Utils.isBitSet(line & Constants.READ_RESIDUAL_UNDERFLOW_FLAG_MASK);
residualOverflow = Utils.isBitSet(line & Constants.RESIDUAL_OVERFLOW_FLAG_MASK);
residualUnderflow = Utils.isBitSet(line & Constants.RESIDUAL_UNDERFLOW_FLAG_MASK);
response = ServiceResponse.valueOf((byte) ((line & Constants.THIRD_BYTE_MASK) >>> Constants.ONE_BYTE_SHIFT));
status = SCSIStatus.valueOf((byte) (line & Constants.FOURTH_BYTE_MASK));
}
/** {@inheritDoc} */
@Override
protected final void deserializeBytes20to23 (final int line) throws InternetSCSIException {
snackTag = line;
}
/** {@inheritDoc} */
@Override
protected final void deserializeBytes36to39 (final int line) throws InternetSCSIException {
expectedDataSequenceNumber = line;
}
/** {@inheritDoc} */
@Override
protected final void deserializeBytes40to43 (final int line) throws InternetSCSIException {
bidirectionalReadResidualCount = line;
}
/** {@inheritDoc} */
@Override
protected final void deserializeBytes44to47 (final int line) throws InternetSCSIException {
residualCount = line;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
protected final void checkIntegrity () throws InternetSCSIException {
String exceptionMessage;
do {
Utils.isReserved(logicalUnitNumber);
if (response != ServiceResponse.COMMAND_COMPLETED_AT_TARGET) {
if (bidirectionalReadResidualOverflow || bidirectionalReadResidualUnderflow || residualOverflow || residualUnderflow) {
exceptionMessage = "Theses bits must to be 0, because the command is not completed at the target.";
break;
}
if (status != SCSIStatus.GOOD) {
exceptionMessage = "Status Code is only valid, because the command is not completed at the target.";
break;
}
}
if (bidirectionalReadResidualOverflow && bidirectionalReadResidualUnderflow) {
exceptionMessage = "The 'o' and 'u' bits must be set mutal exclusion.";
break;
}
if (residualOverflow && residualUnderflow) {
exceptionMessage = "The 'O' and 'U' bits must be set mutal exclusion.";
break;
}
if ((!residualOverflow && !residualUnderflow) && residualCount != 0) {
exceptionMessage = "ResidualCount is only valid either the ResidualOverflow or ResidualUnderflow-Flag is set.";
break;
}
if ((!bidirectionalReadResidualOverflow && !bidirectionalReadResidualUnderflow) && bidirectionalReadResidualCount != 0) {
exceptionMessage = "BidirectionalResidualCount is only valid either the " + "BidirectionalResidualOverflow or BidirectionalResidualUnderflow-Flag is set.";
break;
}
if (response != ServiceResponse.COMMAND_COMPLETED_AT_TARGET && expectedDataSequenceNumber != 0) {
exceptionMessage = "The ExpectedDataSequenceNumber is not valid, because the command is not " + "completed at the target.";
break;
}
// message is checked correctly
return;
} while (false);
throw new InternetSCSIException(exceptionMessage);
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
protected final int serializeBytes1to3 () {
int line = status.value();
line |= response.value() << Constants.ONE_BYTE_SHIFT;
if (residualUnderflow) {
line |= Constants.RESIDUAL_UNDERFLOW_FLAG_MASK;
}
if (residualOverflow) {
line |= Constants.RESIDUAL_OVERFLOW_FLAG_MASK;
}
if (bidirectionalReadResidualUnderflow) {
line |= Constants.READ_RESIDUAL_UNDERFLOW_FLAG_MASK;
}
if (bidirectionalReadResidualOverflow) {
line |= Constants.READ_RESIDUAL_OVERFLOW_FLAG_MASK;
}
return line;
}
/** {@inheritDoc} */
@Override
protected final int serializeBytes20to23 () {
return snackTag;
}
/** {@inheritDoc} */
@Override
protected final int serializeBytes36to39 () {
return expectedDataSequenceNumber;
}
/** {@inheritDoc} */
@Override
protected final int serializeBytes40to43 () {
return bidirectionalReadResidualCount;
}
/** {@inheritDoc} */
@Override
protected final int serializeBytes44to47 () {
return residualCount;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
}