View Javadoc

1   package org.jscsi.target.connection.stage.fullfeature;
2   
3   import java.io.IOException;
4   import java.nio.ByteBuffer;
5   
6   import org.jscsi.exception.InternetSCSIException;
7   import org.jscsi.parser.ProtocolDataUnit;
8   import org.jscsi.parser.scsi.SCSIResponseParser;
9   import org.jscsi.parser.scsi.SCSIStatus;
10  import org.jscsi.target.connection.TargetPduFactory;
11  import org.jscsi.target.connection.phase.TargetFullFeaturePhase;
12  import org.jscsi.target.connection.stage.TargetStage;
13  import org.jscsi.target.scsi.IResponseData;
14  import org.jscsi.target.scsi.ScsiResponseDataSegment;
15  import org.jscsi.target.scsi.sense.AdditionalSenseBytes;
16  import org.jscsi.target.scsi.sense.AdditionalSenseCodeAndQualifier;
17  import org.jscsi.target.scsi.sense.ErrorType;
18  import org.jscsi.target.scsi.sense.FixedFormatSenseData;
19  import org.jscsi.target.scsi.sense.SenseKey;
20  import org.jscsi.target.scsi.sense.information.FourByteInformation;
21  import org.jscsi.target.scsi.sense.senseDataDescriptor.senseKeySpecific.FieldPointerSenseKeySpecificData;
22  
23  /**
24   * This class is an abstract super-class for stages of the {@link TargetFullFeaturePhase}.
25   * 
26   * @see TargetStage
27   * @author Andreas Ergenzinger
28   */
29  public abstract class TargetFullFeatureStage extends TargetStage {
30  
31      /**
32       * The abstract constructor.
33       * 
34       * @param targetFullFeaturePhase
35       *            the phase this stage is a part of
36       */
37      public TargetFullFeatureStage(TargetFullFeaturePhase targetFullFeaturePhase) {
38          super(targetFullFeaturePhase);
39      }
40  
41      /**
42       * Creates a PDU with {@link FixedFormatSenseData} that must be sent to the initiator after receiving a
43       * Command
44       * Descriptor Block with an illegal field.
45       * 
46       * @param senseKeySpecificData
47       *            contains a list of all illegal fields
48       * @param additionalSenseCodeAndQualifier
49       *            provides more specific information about the cause of the check condition
50       * @param initiatorTaskTag
51       *            used by the initiator to identify the task
52       * @param expectedDataTransferLength
53       *            the amount of payload data expected by the initiator (i.e. allocated buffer
54       *            space)
55       * @return the error PDU
56       */
57      protected static final ProtocolDataUnit createFixedFormatErrorPdu(
58          final FieldPointerSenseKeySpecificData[] senseKeySpecificData,
59          final AdditionalSenseCodeAndQualifier additionalSenseCodeAndQualifier, final int initiatorTaskTag,
60          final int expectedDataTransferLength) {
61  
62          // create the whole sense data
63          FixedFormatSenseData senseData = new FixedFormatSenseData(false,// valid
64              ErrorType.CURRENT,// error type
65              false,// file mark
66              false,// end of medium
67              false,// incorrect length indicator
68              SenseKey.ILLEGAL_REQUEST,// sense key
69              new FourByteInformation(),// information
70              new FourByteInformation(),// command specific information
71              additionalSenseCodeAndQualifier,// additional sense code and
72                                              // qualifier
73              (byte)0,// field replaceable unit code
74              senseKeySpecificData[0],// sense key specific data, only report
75                                      // first problem
76              new AdditionalSenseBytes());// additional sense bytes
77  
78          // keep only the part of the sense data that will be sent
79          final ScsiResponseDataSegment dataSegment =
80              new ScsiResponseDataSegment(senseData, expectedDataTransferLength);
81          final int senseDataSize = senseData.size();
82  
83          // calculate residuals and flags
84          final int residualCount = Math.abs(expectedDataTransferLength - senseDataSize);
85          final boolean residualOverflow = expectedDataTransferLength < senseDataSize;
86          final boolean residualUnderflow = expectedDataTransferLength > senseDataSize;
87  
88          // create and return PDU
89          return TargetPduFactory.createSCSIResponsePdu(false,// bidirectionalReadResidualOverflow
90              false,// bidirectionalReadResidualUnderflow
91              residualOverflow,// residualOverflow
92              residualUnderflow,// residualUnderflow,
93              SCSIResponseParser.ServiceResponse.COMMAND_COMPLETED_AT_TARGET,// response,
94              SCSIStatus.CHECK_CONDITION,// status,
95              initiatorTaskTag,// initiatorTaskTag,
96              0,// snackTag
97              0,// expectedDataSequenceNumber
98              0,// bidirectionalReadResidualCount
99              residualCount,// residualCount
100             dataSegment);// data segment
101     }
102 
103     /**
104      * Creates a PDU with {@link FixedFormatSenseData} that must be sent to the initiator after receiving a
105      * Command
106      * Descriptor Block with an illegal field, which requires the the additional sense code
107      * {@link AdditionalSenseCodeAndQualifier#INVALID_FIELD_IN_CDB}.
108      * 
109      * @param senseKeySpecificData
110      *            contains a list of all illegal fields
111      * @param initiatorTaskTag
112      *            used by the initiator to identify the task
113      * @param expectedDataTransferLength
114      *            the amount of payload data expected by the initiator (i.e. allocated buffer
115      *            space)
116      * @return the error PDU
117      */
118     protected static final ProtocolDataUnit createFixedFormatErrorPdu(
119         final FieldPointerSenseKeySpecificData[] senseKeySpecificData, final int initiatorTaskTag,
120         final int expectedDataTransferLength) {
121         return createFixedFormatErrorPdu(senseKeySpecificData,
122             AdditionalSenseCodeAndQualifier.INVALID_FIELD_IN_CDB, initiatorTaskTag,
123             expectedDataTransferLength);
124     }
125 
126     /**
127      * Creates a SCSI Response PDU with a length zero data segment. Objects created with this method can be
128      * used as
129      * replies in task terminating with {@link SCSIStatus#GOOD} which do not require additional data to be
130      * transfered,
131      * or for creating follow-up PDU with {@link SCSIStatus#CHECK_CONDITION} status sent after Data-In PDUs
132      * with sense
133      * data.
134      * 
135      * @param status
136      *            the SCSI status of the task
137      * @param initiatorTaskTag
138      *            used by the initiator to identify the task
139      * @param expectedDataTransferLength
140      *            total amount of payload data in bytes expected by the initiator
141      * @param responseDataSize
142      *            actual amount of payload data in bytes sent by the target
143      * @return the SCSI Response PDU
144      */
145     protected static final ProtocolDataUnit createScsiResponsePdu(final SCSIStatus status,
146         final int initiatorTaskTag, final int expectedDataTransferLength, final int responseDataSize) {
147 
148         // calculate residuals and flags
149         final int residualCount = Math.abs(expectedDataTransferLength - responseDataSize);
150         final boolean residualOverflow = expectedDataTransferLength < responseDataSize;
151         final boolean residualUnderflow = expectedDataTransferLength > responseDataSize;
152 
153         return TargetPduFactory.createSCSIResponsePdu(false,// bidirectionalReadResidualOverflow
154             false,// bidirectionalReadResidualUnderflow
155             residualOverflow,// residualOverflow,
156             residualUnderflow,// residualUnderflow,
157             SCSIResponseParser.ServiceResponse.COMMAND_COMPLETED_AT_TARGET,// response
158             status,// status
159             initiatorTaskTag,// initiatorTaskTag
160             0,// snackTag
161             0,// expectedDataSequenceNumber
162             0,// bidirectionalReadResidualCount
163             residualCount,// residualCount
164             ScsiResponseDataSegment.EMPTY_DATA_SEGMENT);// data segment
165     }
166 
167     /**
168      * Sends a two byte sequence of a Data-In and a SCSI Response PDU with the specified <i>responseData</i>
169      * payload to
170      * the initiator.
171      * 
172      * @param initiatorTaskTag
173      *            used by the initiator to identify the task
174      * @param expectedDataTransferLength
175      *            the total amount of payload data in bytes expected by the initiator
176      *            <p>
177      *            The method might throw exceptions during PDU serialization and sending.
178      * @param responseData
179      *            the data requested by the initiator
180      * @throws InterruptedException
181      * @throws IOException
182      * @throws InternetSCSIException
183      */
184     protected final void sendResponse(final int initiatorTaskTag, final int expectedDataTransferLength,
185         final IResponseData responseData) throws InterruptedException, IOException, InternetSCSIException {
186 
187         // serialize all response data
188         final ByteBuffer fullBuffer = ByteBuffer.allocate(responseData.size());
189         responseData.serialize(fullBuffer, 0);
190         
191         // copy fullBuffer to buffer with size trimmed to
192         // expectedDataTransferLength
193         ByteBuffer trimmedBuffer;
194         if (fullBuffer.capacity() <= expectedDataTransferLength) {
195             // no trimming
196             trimmedBuffer = fullBuffer;
197         } else {
198             trimmedBuffer = ByteBuffer.allocate(expectedDataTransferLength);
199             trimmedBuffer.put(fullBuffer.array(),// source array
200                 0,// offset in source
201                 expectedDataTransferLength);// length
202         }
203 
204         // coompute residual count and associated flags
205         final boolean residualOverflow = expectedDataTransferLength < fullBuffer.capacity();
206         final boolean residualUnderflow = expectedDataTransferLength > fullBuffer.capacity();
207         final int residualCount = Math.abs(expectedDataTransferLength - fullBuffer.capacity());
208 
209         // create and send PDU
210         ProtocolDataUnit pdu = TargetPduFactory.createDataInPdu(true,// finalFlag
211             false,// acknowledgeFlag always false
212             residualOverflow,// residualOverflowFlag x
213             residualUnderflow,// residualUnderflowFlag x
214             true,// statusFlag
215             SCSIStatus.GOOD,// status, reserved
216             0,// logicalUnitNumber reserved
217             initiatorTaskTag,// initiatorTaskTag
218             -1,// targetTransferTag reserved
219             0,// dataSequenceNumber
220             0,// bufferOffset
221             residualCount,// residualCount x
222             trimmedBuffer);// dataSegment
223 
224         connection.sendPdu(pdu);
225         
226         //        VirtualBox was not working with the success message sent via ScsiResponse ..
227         //        Deactivated this method and set the data-in pdu up to take over the success
228         //        notification.
229         //        pdu = TargetPduFactory.createSCSIResponsePdu(false,// bidirectionalReadResidualOverflow
230         //            false,// bidirectionalReadResidualUnderflow
231         //            residualOverflow,// residualOverflow
232         //            residualUnderflow,// residualUnderflow
233         //            ServiceResponse.COMMAND_COMPLETED_AT_TARGET,// response
234         //            SCSIStatus.GOOD,// status
235         //            initiatorTaskTag, 0,// snackTag, reserved
236         //            0,// expectedDataSequenceNumber
237         //            0,// bidirectionalReadResidualCount
238         //            residualCount,// residualCount
239         //            ScsiResponseDataSegment.EMPTY_DATA_SEGMENT);// scsiResponseDataSegment
240         //
241         //        connection.sendPdu(pdu);
242 
243     }
244 }