1 package org.jscsi.target.scsi.cdb; 2 3 4 import java.nio.ByteBuffer; 5 6 import org.jscsi.parser.scsi.SCSICommandParser; 7 import org.jscsi.target.scsi.sense.AdditionalSenseCodeAndQualifier; 8 import org.jscsi.target.scsi.sense.SenseData; 9 import org.jscsi.target.scsi.sense.SenseKey; 10 import org.jscsi.target.scsi.sense.senseDataDescriptor.senseKeySpecific.FieldPointerSenseKeySpecificData; 11 import org.jscsi.target.util.BitManip; 12 13 14 /** 15 * An abstract class for accessing the variables common to Command Descriptor Blocks of all sizes. 16 * <p> 17 * A Command Descriptor Blocks is a blocks of multiple bytes that contains a {@link ScsiOperationCode}, specifying a 18 * command the SCSI initiator wants the SCSI target to perform and related fields, which determine details of the 19 * ordered task. 20 * <p> 21 * Since all fields in the Control byte except for the Normal ACA bit are either reserved, obsolete, or vendor specific, 22 * the NACA bit can be accessed directly from this class. 23 * 24 * @author Andreas Ergenzinger 25 */ 26 public abstract class CommandDescriptorBlock { 27 28 /** 29 * Not a CDB field. This array stores all {@link FieldPointerSenseKeySpecificData} that has to be sent back to the 30 * SCSI initiator as a reaction to the CDB {@link ByteBuffer} passed during initialization of this 31 * {@link CommandDescriptorBlock} instance. 32 * <p> 33 * If, during the parsing process, an illegal value is detected, the array will be initialized and a 34 * {@link FieldPointerSenseKeySpecificData} object, specifying the position of the illegal field, will be added. 35 */ 36 private FieldPointerSenseKeySpecificData[] illegalFieldPointers = null; 37 38 /** 39 * Determines which command to perform. 40 */ 41 private ScsiOperationCode scsiOperationCode; 42 43 /** 44 * Specifies if Normal ACA shall be used. A value of <code>true</code> is not supported by the jSCSI Target. 45 */ 46 private boolean normalAutoContingentAllegiance; 47 48 /** 49 * The abstract constructor. 50 * <p> 51 * Deserializes the first byte containing the SCSI Operation Code and the Control byte. 52 * <p> 53 * The passed {@link ByteBuffer} parameter <b>must</b> have a capacity ≥ the length of the specific command 54 * descriptor block. Since this is assured by the {@link SCSICommandParser#getCDB()} method, which always returns 55 * {@link ByteBuffer} objects with capacity 16, no checks will be performed. 56 * 57 * @param buffer contains the serialized CDB starting at index position zero 58 */ 59 public CommandDescriptorBlock (ByteBuffer buffer) { 60 // SCSI OpCode 61 scsiOperationCode = ScsiOperationCode.valueOf(buffer.get(0)); 62 if (scsiOperationCode == null) // unsupported OpCode 63 addIllegalFieldPointer(0); 64 /* 65 * The above if block should never be entered, since unsupported operation codes in the CDB would have been 66 * discovered during TargetFullFeatureStage selection in TargetFullFeaturePhase.execute(). 67 */ 68 69 // Control (NACA bit, all other fields are obsolete, reserved, or vendor 70 // specific) 71 final CdbType cdbType = scsiOperationCode.getCdbType(); 72 int controlByteIndex; 73 74 switch (cdbType) { 75 case SIX_BYTE_COMMANDS : 76 controlByteIndex = 5; 77 break; 78 case TEN_BYTE_COMMANDS : 79 controlByteIndex = 9; 80 break; 81 case TWELVE_BYTE_COMMANDS : 82 controlByteIndex = 11; 83 break; 84 case SIXTEEN_BYTE_COMMANDS : 85 controlByteIndex = 15; 86 break; 87 default : 88 controlByteIndex = -1; 89 /* 90 * Would lead to ArrayOutOfBoundsException, however, this will not happen, since unsupported group codes 91 * will be filtered in TargetFullFeaturePhase.execute() (see above). 92 */ 93 } 94 95 normalAutoContingentAllegiance = BitManip.getBit(buffer.get(controlByteIndex), 2); 96 if (normalAutoContingentAllegiance) {// normalACA is not supported 97 addIllegalFieldPointer(controlByteIndex, 2); 98 } 99 } 100 101 public final ScsiOperationCode getScsiOperationCode () { 102 return scsiOperationCode; 103 } 104 105 /** 106 * The NACA (Normal ACA) bit specifies whether an auto contingent allegiance (ACA) is established if the command 107 * terminates with CHECK CONDITION status. A NACA bit set to one specifies that an ACA shall be established. A NACA 108 * bit set to zero specifies that an ACA shall not be established. 109 * 110 * @return <code>true</code> if the Normal ACA bit in the CDB's Control byte is set and <code>false</code> if it is 111 * not. 112 */ 113 public final boolean isNormalACA () { 114 return normalAutoContingentAllegiance; 115 } 116 117 /** 118 * Adds an instance {@link FieldPointerSenseKeySpecificData}, which specifies an illegal field by the position of 119 * its first byte, to {@link #illegalFieldPointers}. 120 * 121 * @param byteNumber index of the first byte of the illegal field 122 */ 123 protected final void addIllegalFieldPointer (int byteNumber) { 124 final FieldPointerSenseKeySpecificData fp = new FieldPointerSenseKeySpecificData(true,// senseKeySpecificDataValid 125 true,// commandData (i.e. invalid field in CDB) 126 false,// bitPointerValid 127 0,// bitPointer 128 byteNumber);// fieldPointer 129 addIllegalFieldPointer(fp); 130 } 131 132 /** 133 * Adds an instance {@link FieldPointerSenseKeySpecificData}, which specifies an illegal field by the position of 134 * its first byte and its first bit, to {@link #illegalFieldPointers}. 135 * 136 * @param byteNumber index of the first byte of the illegal field 137 * @param bitNumber index of the first bit of the illegal field 138 */ 139 protected final void addIllegalFieldPointer (int byteNumber, int bitNumber) { 140 FieldPointerSenseKeySpecificData fp = new FieldPointerSenseKeySpecificData(true,// senseKeySpecificDataValid 141 true,// commandData (i.e. invalid field in CDB) 142 true,// bitPointerValid 143 bitNumber,// bitPointer 144 byteNumber);// fieldPointer 145 addIllegalFieldPointer(fp); 146 } 147 148 /** 149 * Adds a {@link FieldPointerSenseKeySpecificData} object to {@link #illegalFieldPointers}. Initializes and grows 150 * the array if necessary. 151 * 152 * @param illegalFieldPointer the object to add 153 */ 154 private final void addIllegalFieldPointer (final FieldPointerSenseKeySpecificData illegalFieldPointer) { 155 // grow array? 156 if (illegalFieldPointers == null) illegalFieldPointers = new FieldPointerSenseKeySpecificData[10]; 157 final int size = getIllegalFieldPointerSize(); 158 if (size >= illegalFieldPointers.length) { 159 // grow 160 FieldPointerSenseKeySpecificData[] temp = new FieldPointerSenseKeySpecificData[illegalFieldPointers.length + 1]; 161 for (int i = 0; i < size; ++i) { 162 temp[i] = illegalFieldPointers[i]; 163 } 164 illegalFieldPointers = temp; 165 } 166 // add new element 167 illegalFieldPointers[size] = illegalFieldPointer; 168 } 169 170 /** 171 * Returns the number of elements stored in {@link #illegalFieldPointers}. 172 * 173 * @return the number of elements stored in {@link #illegalFieldPointers} 174 */ 175 private final int getIllegalFieldPointerSize () { 176 if (illegalFieldPointers == null) return 0; 177 int size = 0; 178 while (size < illegalFieldPointers.length) { 179 if (illegalFieldPointers[size] != null) 180 ++size; 181 else 182 break; 183 } 184 return size; 185 } 186 187 /** 188 * Returns <code>null</code> if there were no illegal fields in the serialized CDB passed to the constructor, or an 189 * array of appropriate {@link FieldPointerSenseKeySpecificData}, that have to be enclosed in the {@link SenseData} 190 * returned to the initiator (with sense key {@link SenseKey#ILLEGAL_REQUEST} and additional sense code and sense 191 * code qualifier {@link AdditionalSenseCodeAndQualifier#INVALID_FIELD_IN_CDB}). 192 * 193 * @return <code>null</code> or an array of appropriate {@link FieldPointerSenseKeySpecificData} 194 */ 195 public final FieldPointerSenseKeySpecificData[] getIllegalFieldPointers () { 196 if (illegalFieldPointers == null) return null; 197 // returned trimmed array without null values 198 final int size = getIllegalFieldPointerSize(); 199 FieldPointerSenseKeySpecificData[] result = new FieldPointerSenseKeySpecificData[size]; 200 for (int i = 0; i < size; ++i) { 201 result[i] = illegalFieldPointers[i]; 202 } 203 return result; 204 } 205 }