
org.jscsi.parser.scsi.SCSICommandParser 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.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).
*
*
*
* Value
* Meaning
*
*
* 0
* Reserved
*
*
* 1
* Extended CDB
*
*
* 2
* Expected Bidirectional Read Data Length
*
*
* 3 - 63
* Reserved
*
*
*/
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();
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
}