/** * 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.scsi; import java.nio.ByteBuffer; import org.jscsi.exception.InternetSCSIException; import org.jscsi.parser.Constants; import org.jscsi.parser.InitiatorMessageParser; import org.jscsi.parser.ProtocolDataUnit; import org.jscsi.parser.datasegment.DataSegmentFactory.DataSegmentFormat; import org.jscsi.utils.ByteBufferCache; import org.jscsi.utils.Utils; import com.carrotsearch.hppc.ByteObjectOpenHashMap; /** * <h1>SCSICommandParser</h1> * <p> * This class parses a SCSI Command message defined in the iSCSI Standard (RFC3720). * <p> * <h4>CmdSN - Command Sequence Number</h4> Enables ordered delivery across multiple connections in a single * session. * <p> * <h4>ExpStatSN</h4> Command responses up to <code>ExpStatSN - 1 (mod 2**32)</code> have been received * (acknowledges status) on the connection. * <p> * <h4>Data Segment - Command Data</h4> Some SCSI commands require additional parameter data to accompany the * SCSI command. This data may be placed beyond the boundary of the iSCSI header in a data segment. * Alternatively, user data (e.g., from a WRITE operation) can be placed in the data segment (both cases are * referred to as immediate data). These data are governed by the rules for solicited vs. unsolicited data * outlined in Section 3.2.4.2 Data Transfer Overview. * * @author Volker Wildi */ public class SCSICommandParser extends InitiatorMessageParser { // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** * This enumeration defines all valid types of additional header segments, * which are defined by the iSCSI standard (RFC3720). * <p> * <table border="1"> * <tr> * <th>Value</th> * <th>Meaning</th> * </tr> * <tr> * <td>0</td> * <td>Reserved</td> * </tr> * <tr> * <td>1</td> * <td>Extended CDB</td> * </tr> * <tr> * <td>2</td> * <td>Expected Bidirectional Read Data Length</td> * </tr> * <tr> * <td>3 - 63</td> * <td>Reserved</td> * </tr> * </table> */ public enum TaskAttributes { /** * Untagged queuing allows a target to accept a command from an * initiator for a logical unit or target routine while I/O processes * from other initiators are being executed. Only one command for each * I_T_x nexus shall be accepted at a time. */ UNTAGGED((byte)0), /** * If only SIMPLE QUEUE TAG messages are used, the target may execute * the commands in any order that is deemed desirable within the * constraints of the queue management algorithm specified in the * control mode page (see [SAM2, 8.3.3.1]). */ SIMPLE((byte)1), /** * If ORDERED QUEUE TAG messages are used, the target shall execute the * commands in the order received with respect to other commands * received with ORDERED QUEUE TAG messages. All commands received with * a SIMPLE QUEUE TAG message prior to a command received with an * ORDERED QUEUE TAG message, regardless of initiator, shall be executed * before that command with the ORDERED QUEUE TAG message. All commands * received with a SIMPLE QUEUE TAG message after a command received * with an ORDERED QUEUE TAG message, regardless of initiator, shall be * executed after that command with the ORDERED QUEUE TAG message. */ ORDERED((byte)2), /** * A command received with a HEAD OF QUEUE TAG message is placed first * in the queue, to be executed next. A command received with a HEAD OF * QUEUE TAG message shall be executed prior to any queued I/O process. * Consecutive commands received with HEAD OF QUEUE TAG messages are * executed in a last-in-first-out order. */ HEAD_OF_QUEUE((byte)3), /** ACA is Auto Contingent Allegiance. */ ACA((byte)4); private final byte value; private static ByteObjectOpenHashMap<TaskAttributes> mapping; static { TaskAttributes.mapping = new ByteObjectOpenHashMap<TaskAttributes>(values().length); for (TaskAttributes s : values()) { TaskAttributes.mapping.put(s.value, s); } } private TaskAttributes(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 <code>value</code>. * * @param value * The value to search for. * @return The constant defined for the given <code>value</code>. Or <code>null</code>, if this value * is not defined by this * enumeration. */ public static final TaskAttributes valueOf(final byte value) { return TaskAttributes.mapping.get(value); } } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** Bit mask to indicate data input (read) is expected. */ private static final int READ_EXPECTED_FLAG_MASK = 0x00400000; /** Bit mask to indicate data output (write) is expected. */ private static final int WRITE_EXPECTED_FLAG_MASK = 0x00200000; /** Bit mask to extract the task attributes. */ private static final int TASK_ATTRIBUTES_FLAG_MASK = 0x00070000; /** The size (in bytes) of a normal CDB <code>ByteBuffer</code>. */ private static final int CDB_SIZE = 16; // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** The expected data transfer length. */ private int expectedDataTransferLength; /** SCSI Command Descriptor Block. */ private ByteBuffer commandDescriptorBlock; /** The flag 'R' (Input data expected). */ private boolean readExpectedFlag; /** The flag 'W' (Output data expected). */ private boolean writeExpectedFlag; /** The task attributes. */ private TaskAttributes taskAttributes; // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** * Default constructor, creates a new, empty <code>SCSICommandParser</code> object. * * @param initProtocolDataUnit * The reference <code>ProtocolDataUnit</code> instance, which * contains this <code>SCSICommandParser</code> subclass object. */ public SCSICommandParser(final ProtocolDataUnit initProtocolDataUnit) { super(initProtocolDataUnit); commandDescriptorBlock = ByteBufferCache.allocate(CDB_SIZE); } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** {@inheritDoc} */ @Override public final String toString() { final StringBuilder sb = new StringBuilder(Constants.LOG_INITIAL_SIZE); Utils.printField(sb, "LUN", logicalUnitNumber, 1); Utils.printField(sb, "Expected Data Transfer Length", expectedDataTransferLength, 1); sb.append(super.toString()); Utils.printField(sb, "SCSI CDB", commandDescriptorBlock, 1); return sb.toString(); } /** {@inheritDoc} */ @Override public final DataSegmentFormat getDataSegmentFormat() { return DataSegmentFormat.BINARY; } /** {@inheritDoc} */ @Override public final boolean canContainAdditionalHeaderSegments() { return true; } /** {@inheritDoc} */ @Override public final void clear() { super.clear(); readExpectedFlag = false; writeExpectedFlag = false; taskAttributes = null; expectedDataTransferLength = 0x00000000; commandDescriptorBlock.clear(); } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** * There are <code>16</code> bytes in the CDB field to accommodate the * commonly used CDBs. Whenever the CDB is larger than <code>16</code> bytes, an Extended CDB AHS MUST be * used to contain the CDB spillover. * * @return A <code>ByteBuffer</code> with the content of the Command * Descriptor Blocks contained in this <code>SCSICommandParser</code> object. */ public final ByteBuffer getCDB() { return (ByteBuffer)commandDescriptorBlock.rewind(); } /** * For unidirectional operations, the Expected Data Transfer Length field * contains the number of bytes of data involved in this SCSI operation. For * a unidirectional write operation (W flag set to <code>1</code> and R flag * set to <code>0</code>), the initiator uses this field to specify the * number of bytes of data it expects to transfer for this operation. For a * unidirectional read operation (W flag set to <code>0</code> and R flag * set to <code>1</code>), the initiator uses this field to specify the * number of bytes of data it expects the target to transfer to the * initiator. It corresponds to the SAM2 byte count. * <p> * For bidirectional operations (both R and W flags are set to <code>1</code> ), this field contains the * number of data bytes involved in the write transfer. For bidirectional operations, an additional header * segment MUST be present in the header sequence that indicates the Bidirectional Read Expected Data * Transfer Length. The Expected Data Transfer Length field and the Bidirectional Read Expected Data * Transfer Length field correspond to the SAM2 byte count. * <p> * If the Expected Data Transfer Length for a write and the length of the immediate data part that follows * the command (if any) are the same, then no more data PDUs are expected to follow. In this case, the F * bit MUST be set to <code>1</code>. * <p> * If the Expected Data Transfer Length is higher than the FirstBurstLength (the negotiated maximum amount * of unsolicited data the target will accept), the initiator MUST send the maximum amount of unsolicited * data OR ONLY the immediate data, if any. * <p> * Upon completion of a data transfer, the target informs the initiator (through residual counts) of how * many bytes were actually processed (sent and/or received) by the target. * * @return The Expected Data Transfer Length of this SCSICommandParser * object. */ public final int getExpectedDataTransferLength() { return expectedDataTransferLength; } /** * The command expects input data (read). * * @return <code>true</code>,if a read command is expected. Else <code>false</code>. */ public final boolean isReadExpectedFlag() { return readExpectedFlag; } /** * @param newReadExpectedFlag * The readExpectedFlag to set. */ public final void setReadExpectedFlag(final boolean newReadExpectedFlag) { readExpectedFlag = newReadExpectedFlag; } /** * @return Returns the taskAttributes. */ public final TaskAttributes getTaskAttributes() { return taskAttributes; } /** * @param newTaskAttributes * The taskAttributes to set. */ public final void setTaskAttributes(final TaskAttributes newTaskAttributes) { taskAttributes = newTaskAttributes; } /** * The command expects output data (write). * * @return <code>true</code>,if a write command is expected. Else <code>false</code>. */ public final boolean isWriteExpectedFlag() { return writeExpectedFlag; } /** * @param newWriteExpectedFlag * The writeExpectedFlag to set. */ public final void setWriteExpectedFlag(final boolean newWriteExpectedFlag) { writeExpectedFlag = newWriteExpectedFlag; } /** * Sets the new Command Descriptor Block. * * @param newCDB * The new Command Descriptor Block. */ public final void setCommandDescriptorBlock(final ByteBuffer newCDB) { if (newCDB.limit() - newCDB.position() > CDB_SIZE) { throw new IllegalArgumentException( "Buffer cannot be longer than 16 bytes, because AHS-support is not implemented."); } ByteBufferCache.release(commandDescriptorBlock); commandDescriptorBlock = newCDB; } /** * @param newExpectedDataTransferLength * The expectedDataTransferLength to set. */ public final void setExpectedDataTransferLength(final int newExpectedDataTransferLength) { expectedDataTransferLength = newExpectedDataTransferLength; } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** {@inheritDoc} */ @Override protected final void deserializeBytes1to3(final int line) throws InternetSCSIException { readExpectedFlag = Utils.isBitSet(line & READ_EXPECTED_FLAG_MASK); writeExpectedFlag = Utils.isBitSet(line & WRITE_EXPECTED_FLAG_MASK); taskAttributes = TaskAttributes.valueOf((byte)((line & TASK_ATTRIBUTES_FLAG_MASK) >> Constants.TWO_BYTES_SHIFT)); Utils.isReserved(line & Constants.LAST_TWO_BYTES_MASK); } /** {@inheritDoc} */ @Override protected final void deserializeBytes20to23(final int line) throws InternetSCSIException { expectedDataTransferLength = line; } /** {@inheritDoc} */ @Override protected final void deserializeBytes32to35(final int line) throws InternetSCSIException { commandDescriptorBlock.rewind(); commandDescriptorBlock.putInt(line); } /** {@inheritDoc} */ @Override protected final void deserializeBytes36to39(final int line) throws InternetSCSIException { commandDescriptorBlock.putInt(line); } /** {@inheritDoc} */ @Override protected final void deserializeBytes40to43(final int line) throws InternetSCSIException { commandDescriptorBlock.putInt(line); } /** {@inheritDoc} */ @Override protected final void deserializeBytes44to47(final int line) throws InternetSCSIException { commandDescriptorBlock.putInt(line); } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** {@inheritDoc} */ @Override protected final void checkIntegrity() throws InternetSCSIException { String exceptionMessage; do { if (!writeExpectedFlag && !protocolDataUnit.getBasicHeaderSegment().isFinalFlag()) { exceptionMessage = "W and F flag cannot both be 0."; break; } if (expectedDataTransferLength != 0 && !(readExpectedFlag || writeExpectedFlag)) { exceptionMessage = "The ExpectedDataTransferLength is greater than 0, so Read or/and Write Flag has to be set."; break; } // message is checked correctly return; } while (false); throw new InternetSCSIException(exceptionMessage); } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** {@inheritDoc} */ @Override protected final int serializeBytes1to3() { int line = 0; line |= taskAttributes.value() << Constants.TWO_BYTES_SHIFT; if (writeExpectedFlag) { line |= WRITE_EXPECTED_FLAG_MASK; } if (readExpectedFlag) { line |= READ_EXPECTED_FLAG_MASK; } return line; } /** {@inheritDoc} */ @Override protected final int serializeBytes20to23() { return expectedDataTransferLength; } /** {@inheritDoc} */ @Override protected final int serializeBytes32to35() { commandDescriptorBlock.rewind(); return commandDescriptorBlock.getInt(); } /** {@inheritDoc} */ @Override protected final int serializeBytes36to39() { return commandDescriptorBlock.getInt(); } /** {@inheritDoc} */ @Override protected final int serializeBytes40to43() { return commandDescriptorBlock.getInt(); } /** {@inheritDoc} */ @Override protected final int serializeBytes44to47() { return commandDescriptorBlock.getInt(); } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- }