
org.jscsi.parser.data.DataInParser 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.data;
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.parser.scsi.SCSIStatus;
import org.jscsi.utils.Utils;
/**
* This class parses a Data-In message defined in the iSCSI Standard (RFC3720).
*
* @author Volker Wildi
*/
public class DataInParser extends TargetMessageParser {
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** Acknowledge flag mask. */
private static final int ACKNOWLEDGE_FLAG_MASK = 0x00400000;
/** Status flag mask to extract. */
private static final int STATUS_FLAG_MASK = 0x00010000;
/** Bit mask, where the 11th, 12th and the 13th bit are set. */
private static final int BIT_11_TO_13_FLAG_MASK = 0x00380000;
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** The acknowledge Flag. */
private boolean acknowledgeFlag;
/** The Bidirectional Read Residual Overflow Flag (o bit). */
private boolean bidirectionalReadResidualOverflow;
/** The Bidirectional Read Residual Underflow Flag (u bit). */
private boolean bidirectionalReadResidualUnderflow;
/** The Residual Overflow Flag (O bit). */
private boolean residualOverflow;
/** The Residual Underflow Flag (U bit). */
private boolean residualUnderflow;
/** The Status Flag (S bit). */
private boolean statusFlag;
/** The status code. */
private SCSIStatus status;
/** The Data Sequence Number. */
private int dataSequenceNumber;
/** The Buffer Offset. */
private int bufferOffset;
/** The Residual Count. */
private int residualCount;
/** The Target Transfer Tag. */
private int targetTransferTag;
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* Default constructor, creates a new, empty DataInParser object.
*
* @param initProtocolDataUnit The reference ProtocolDataUnit
instance, which contains this
* DataInParser
subclass object.
*/
public DataInParser (final ProtocolDataUnit initProtocolDataUnit) {
super(initProtocolDataUnit);
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
public String getShortInfo () {
return super.getShortInfo() + ", dataSN: " + dataSequenceNumber + ", bufferOffset: " + bufferOffset;
}
/** {@inheritDoc} */
@Override
public String toString () {
final StringBuilder sb = new StringBuilder(Constants.LOG_INITIAL_SIZE);
Utils.printField(sb, "StatusFlag", statusFlag, 1);
Utils.printField(sb, "Status", status.value(), 1);
Utils.printField(sb, "LUN", logicalUnitNumber, 1);
Utils.printField(sb, "Target Task tag", targetTransferTag, 1);
Utils.printField(sb, "StatSN", statusSequenceNumber, 1);
Utils.printField(sb, "MaxCmdSN", maximumCommandSequenceNumber, 1);
Utils.printField(sb, "DataSN", dataSequenceNumber, 1);
Utils.printField(sb, "Buffer Offset", bufferOffset, 1);
Utils.printField(sb, "Residual Count", residualCount, 1);
sb.append(super.toString());
return sb.toString();
}
/** {@inheritDoc} */
@Override
public DataSegmentFormat getDataSegmentFormat () {
return DataSegmentFormat.BINARY;
}
/** {@inheritDoc} */
@Override
public void clear () {
super.clear();
acknowledgeFlag = false;
bidirectionalReadResidualOverflow = false;
bidirectionalReadResidualUnderflow = false;
residualOverflow = false;
residualUnderflow = false;
statusFlag = false;
status = null;
dataSequenceNumber = 0;
bufferOffset = 0;
residualCount = 0;
targetTransferTag = 0;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
public final boolean incrementSequenceNumber () {
return isStatusFlag();
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* The Buffer Offset field contains the offset of this PDU payload data within the complete data transfer. The sum
* of the buffer offset and length should not exceed the expected transfer length for the command.
* The order of data PDUs within a sequence is determined by DataPDUInOrder. When set to Yes, it means that PDUs
* have to be in increasing Buffer Offset order and overlays are forbidden.
* The ordering between sequences is determined by DataSequenceInOrder. When set to Yes, it means that sequences
* have to be in increasing Buffer Offset order and overlays are forbidden.
*
* @return The buffer offset of this DataInParser object.
*/
public int getBufferOffset () {
return bufferOffset;
}
/**
* For input (read) or bidirectional Data-In PDUs, the DataSN is the input PDU number within the data transfer for
* the command identified by the Initiator Task Tag.
* R2T and Data-In PDUs, in the context of bidirectional commands, share the numbering sequence (see Section 3.2.2.3
* Data Sequencing).
* For output (write) data PDUs, the DataSN is the Data-Out PDU number within the current output sequence. The
* current output sequence is either identified by the Initiator Task Tag (for unsolicited data) or is a data
* sequence generated for one R2T (for data solicited through R2T).
*
* @return The Data Sequence Number of this DataInParser object.
*/
public int getDataSequenceNumber () {
return dataSequenceNumber;
}
/**
* 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 object.
*/
public int getResidualCount () {
return residualCount;
}
/**
* On outgoing data, the Target Transfer Tag is provided to the target if the transfer is honoring an R2T. In this
* case, the Target Transfer Tag field is a replica of the Target Transfer Tag provided with the R2T.
* On incoming data, the Target Transfer Tag and LUN MUST be provided by the target if the A bit is set to
* 1
; otherwise they are reserved. The Target Transfer Tag and LUN are copied by the initiator into the
* SNACK of type DataACK that it issues as a result of receiving a SCSI Data-In PDU with the A bit set to
* 1
.
* The Target Transfer Tag values are not specified by this protocol except that the value 0xffffffff
* is reserved and means that the Target Transfer Tag is not supplied. If the Target Transfer Tag is provided, then
* the LUN field MUST hold a valid value and be consistent with whatever was specified with the command; otherwise,
* the LUN field is reserved.
*
* @return Returns the Target Transfer Tag of this DataInParser object.
*/
public int getTargetTaskTag () {
return targetTransferTag;
}
/**
* For sessions with ErrorRecoveryLevel 1
or higher, the target sets this bit to 1
to
* indicate that it requests a positive acknowledgement from the initiator for the data received. The target should
* use the A bit moderately; it MAY only set the A bit to 1
once every MaxBurstLength bytes, or on the
* last Data-In PDU that concludes the entire requested read data transfer for the task from the target’s
* perspective, and it MUST NOT do so more frequently. The target MUST NOT set to 1
the A
* bit for sessions with ErrorRecoveryLevel=0
. The initiator MUST ignore the A bit
*
* On receiving a Data-In PDU with the A bit set to 1
on a session with ErrorRecoveryLevel greater than
* 0
, if there are no holes in the read data until that Data-In PDU, the initiator MUST issue a SNACK
* of type DataACK except when it is able to acknowledge the status for the task immediately via ExpStatSN on other
* outbound PDUs if the status for the task is also received. In the latter case (acknowledgement through
* ExpStatSN), sending a SNACK of type DataACK in response to the A bit is OPTIONAL, but if it is done, it must not
* be sent after the status acknowledgement through ExpStatSN. If the initiator has detected holes in the read data
* prior to that Data-In PDU, it MUST postpone issuing the SNACK of type DataACK until the holes are filled. An
* initiator also MUST NOT acknowledge the status for the task before those holes are filled. A status
* acknowledgement for a task that generated the Data-In PDUs is considered by the target as an implicit
* acknowledgement of the Data-In PDUs if such an acknowledgement was requested by the target.
*
* @return Returns true
, if the AcknowledgeBit is set. Else false
.
*/
public boolean isAcknowledgeFlag () {
return acknowledgeFlag;
}
/**
* 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 the ReadResidualOverflow-Flag of this object is set. Else false
.
*/
public boolean isBidirectionalReadResidualOverflow () {
return bidirectionalReadResidualOverflow;
}
/**
* 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 the ReadResidualUnderflow-Flag of this object is set. Else false
.
*/
public boolean isBidirectionalReadResidualUnderflow () {
return bidirectionalReadResidualUnderflow;
}
/**
* 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 the ResidualOverflow-Flag of this object is set. Else false
.
*/
public boolean isResidualOverflow () {
return residualOverflow;
}
/**
* 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 the ResidualUnderflow-Flag of this object is set. Else false
.
*/
public boolean isResidualUnderflow () {
return residualUnderflow;
}
/**
* 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.
*
* If a SCSI device error is detected while data from the initiator is still expected (the command PDU did not
* contain all the data and the target has not received a Data PDU with the final bit Set), the target MUST wait
* until it receives a Data PDU with the F bit set in the last expected sequence before sending the Response PDU.
*
* @return The status code of this object.
* @see SCSIStatus
*/
public SCSIStatus getStatus () {
return status;
}
/**
* Set this to indicate that the Command Status field contains status. If this bit is set to 1
, the
* F bit
MUST also be set to 1
.
* The fields StatSN, Status, and Residual Count only have meaningful content if the S bit is set to 1
* and their values are defined in Section 10.4 SCSI Response.
*
* @return True
, if the Status-Flag of this object is set. Else false
.
*/
public boolean isStatusFlag () {
return statusFlag;
}
public void setAcknowledgeFlag (boolean acknowledgeFlag) {
this.acknowledgeFlag = acknowledgeFlag;
}
public void setResidualOverflowFlag (boolean residualOverflowFlag) {
this.residualOverflow = residualOverflowFlag;
}
public void setResidualUnderflowFlag (boolean residualUnderflowFlag) {
this.residualUnderflow = residualUnderflowFlag;
}
public void setStatusFlag (boolean statusFlag) {
this.statusFlag = statusFlag;
}
public void setStatus (SCSIStatus status) {
this.status = status;
}
public void setTargetTransferTag (int targetTransferTag) {
this.targetTransferTag = targetTransferTag;
}
public void setDataSequenceNumber (int dataSequenceNumber) {
this.dataSequenceNumber = dataSequenceNumber;
}
public void setBufferOffset (int bufferOffset) {
this.bufferOffset = bufferOffset;
}
public void setResidualCount (int residualCount) {
this.residualCount = residualCount;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
protected void deserializeBytes1to3 (final int line) throws InternetSCSIException {
acknowledgeFlag = Utils.isBitSet(line & ACKNOWLEDGE_FLAG_MASK);
residualOverflow = Utils.isBitSet(line & Constants.RESIDUAL_OVERFLOW_FLAG_MASK);
residualUnderflow = Utils.isBitSet(line & Constants.RESIDUAL_UNDERFLOW_FLAG_MASK);
statusFlag = Utils.isBitSet(line & STATUS_FLAG_MASK);
Utils.isReserved(line & BIT_11_TO_13_FLAG_MASK);
Utils.isReserved(line & Constants.THIRD_BYTE_MASK);
status = SCSIStatus.valueOf((byte) (line & Constants.FOURTH_BYTE_MASK));
}
/** {@inheritDoc} */
@Override
protected void deserializeBytes20to23 (final int line) throws InternetSCSIException {
targetTransferTag = line;
}
/** {@inheritDoc} */
@Override
protected void deserializeBytes36to39 (final int line) throws InternetSCSIException {
dataSequenceNumber = line;
}
/** {@inheritDoc} */
@Override
protected void deserializeBytes40to43 (final int line) throws InternetSCSIException {
bufferOffset = line;
}
/** {@inheritDoc} */
@Override
protected void deserializeBytes44to47 (final int line) throws InternetSCSIException {
residualCount = line;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
protected void checkIntegrity () throws InternetSCSIException {
String exceptionMessage;
do {
if (statusFlag && !protocolDataUnit.getBasicHeaderSegment().isFinalFlag()) {
exceptionMessage = "FinalFlag must also be set, if the StatusFlag is set.";
break;
}
if (!statusFlag && (bidirectionalReadResidualOverflow || bidirectionalReadResidualUnderflow) && (residualOverflow || residualUnderflow)) {
exceptionMessage = "The StatusFlag must be set, if any flags are set.";
break;
}
if (acknowledgeFlag && (targetTransferTag == 0 && logicalUnitNumber == 0)) {
exceptionMessage = "If the AcknowledgeFlag is set, the TargetTaskTag" + " and the LogicalUnitNumber must be unequal 0.";
break;
}
if (!acknowledgeFlag && (targetTransferTag != 0 && logicalUnitNumber != 0)) {
exceptionMessage = "The TargetTransferTag and LogicalUnitNumber must" + " be reserved, because the AcknowledgeFlag is not set.";
break;
}
// message is checked correctly
return;
} while (false);
throw new InternetSCSIException(exceptionMessage);
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
protected int serializeBytes1to3 () {
int line = status.value();
if (acknowledgeFlag) {
line |= ACKNOWLEDGE_FLAG_MASK;
}
if (residualOverflow) {
line |= Constants.READ_RESIDUAL_OVERFLOW_FLAG_MASK;
}
if (residualUnderflow) {
line |= Constants.READ_RESIDUAL_UNDERFLOW_FLAG_MASK;
}
if (statusFlag) {
line |= STATUS_FLAG_MASK;
}
return line;
}
/** {@inheritDoc} */
@Override
protected int serializeBytes20to23 () {
return targetTransferTag;
}
/** {@inheritDoc} */
@Override
protected int serializeBytes36to39 () {
return dataSequenceNumber;
}
/** {@inheritDoc} */
@Override
protected int serializeBytes40to43 () {
return bufferOffset;
}
/** {@inheritDoc} */
@Override
protected int serializeBytes44to47 () {
return residualCount;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
}