/** * 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 <COPYRIGHT HOLDER> 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 <code>ProtocolDataUnit</code> instance, which * contains this <code>DataInParser</code> 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.<br/> * 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.<br/> * 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. <br/> * R2T and Data-In PDUs, in the context of bidirectional commands, share the * numbering sequence (see Section 3.2.2.3 Data Sequencing). <br/> * 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. <br/> * On incoming data, the Target Transfer Tag and LUN MUST be provided by the * target if the A bit is set to <code>1</code>; 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 <code>1</code>.<br/> * The Target Transfer Tag values are not specified by this protocol except * that the value <code>0xffffffff</code> 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 <code>1</code> or higher, the target * sets this bit to <code>1</code> 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 <code>1</code> 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 <code>1</code> the <code>A</code> bit for sessions * with ErrorRecoveryLevel=<code>0</code>. The initiator MUST ignore the A * bit <br/> * <p> * On receiving a Data-In PDU with the A bit set to <code>1</code> on a session with ErrorRecoveryLevel * greater than <code>0</code>, 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 <code>true</code>, if the AcknowledgeBit is set. Else <code>false</code>. */ 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 <code>True</code>, if the ReadResidualOverflow-Flag of this * object is set. Else <code>false</code>. */ 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 <code>True</code>, if the ReadResidualUnderflow-Flag of this * object is set. Else <code>false</code>. */ 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 <code>True</code>, if the ResidualOverflow-Flag of this object is * set. Else <code>false</code>. */ 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 <code>True</code>, if the ResidualUnderflow-Flag of this object * is set. Else <code>false</code>. */ 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. * <p> * 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 <code>1</code>, the <code>F bit</code> MUST also be * set to <code>1</code>.<br/> * The fields StatSN, Status, and Residual Count only have meaningful * content if the S bit is set to <code>1</code> and their values are * defined in Section 10.4 SCSI Response. * * @return <code>True</code>, if the Status-Flag of this object is set. Else <code>false</code>. */ 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; } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- }