View Javadoc

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 &ge; 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 }