All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jscsi.parser.scsi.SCSICommandParser Maven / Gradle / Ivy

The newest version!
/**
 * 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.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;

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.Utils;


/**
 * 

SCSICommandParser

*

* This class parses a SCSI Command message defined in the iSCSI Standard (RFC3720). *

*

CmdSN - Command Sequence Number

Enables ordered delivery across multiple connections in a single session. *

*

ExpStatSN

Command responses up to ExpStatSN - 1 (mod 2**32) have been received (acknowledges * status) on the connection. *

*

Data Segment - Command Data

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). *

*

* * * * * * * * * * * * * * * * * * * * *
ValueMeaning
0Reserved
1Extended CDB
2Expected Bidirectional Read Data Length
3 - 63Reserved
*/ 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 Map mapping; static { TaskAttributes.mapping = new HashMap(); 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 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 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 ByteBuffer. */ 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 SCSICommandParser object. * * @param initProtocolDataUnit The reference ProtocolDataUnit instance, which contains this * SCSICommandParser subclass object. */ public SCSICommandParser (final ProtocolDataUnit initProtocolDataUnit) { super(initProtocolDataUnit); commandDescriptorBlock = ByteBuffer.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 16 bytes in the CDB field to accommodate the commonly used CDBs. Whenever the CDB is * larger than 16 bytes, an Extended CDB AHS MUST be used to contain the CDB spillover. * * @return A ByteBuffer with the content of the Command Descriptor Blocks contained in this * SCSICommandParser 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 1 and R flag * set to 0), 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 0 and R flag set to * 1), 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. *

* For bidirectional operations (both R and W flags are set to 1 ), 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. *

* 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 1. *

* 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. *

* 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 true,if a read command is expected. Else false. */ 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 true,if a write command is expected. Else false. */ 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."); } 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(); } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy