1 package org.jscsi.target.scsi;
2
3
4 import java.nio.ByteBuffer;
5
6 import org.jscsi.target.connection.TargetPduFactory;
7 import org.jscsi.target.scsi.sense.SenseData;
8 import org.jscsi.target.util.Debug;
9 import org.jscsi.target.util.ReadWrite;
10 import org.slf4j.Logger;
11 import org.slf4j.LoggerFactory;
12
13
14 /**
15 * Instances of this class represent data that may be sent in a SCSI Command Response PDU.
16 * <p>
17 * It may contain either {@link SenseData}, {@link IResponseData}, or (rarely) both. Some SCSI commands require that no
18 * response data is sent (i.e. data segment length = 0) - in these cases the {@link #EMPTY_DATA_SEGMENT} shall be used.
19 * <p>
20 * Since the target must honor the buffer size the initiator has allocated for data returned in the data segment, the
21 * {@link #serialize()} method will only write as many bytes to the passed {@link ByteBuffer} as specified during
22 * initialization.
23 *
24 * @see TargetPduFactory
25 * @author Andreas Ergenzinger
26 */
27 public final class ScsiResponseDataSegment {
28
29 private static final Logger LOGGER = LoggerFactory.getLogger(ScsiResponseDataSegment.class);
30
31 /**
32 * A {@link ScsiResponseDataSegment} of length zero.
33 */
34 public static final ScsiResponseDataSegment EMPTY_DATA_SEGMENT = new ScsiResponseDataSegment();
35
36 /**
37 * The length in bytes of the SENSE LENGTH field.
38 */
39 private static final int SENSE_LENGTH_FIELD_LENGTH = 2;
40
41 /**
42 * The contained {@link SenseData}.
43 */
44 private final SenseData senseData;
45
46 /**
47 * The contained {@link IResponseData}.
48 */
49 private final IResponseData responseData;
50
51 /**
52 * The number of bytes the initiator has allocated for data sent in the data segment, i.e. the maximum number of
53 * bytes of this object that may be transmitted.
54 */
55 private final int allocationLength;
56
57 /**
58 * The length of the {@link ScsiResponseDataSegment}, without considering any limitations due to
59 * {@link #allocationLength}, or <code>-1</code>.
60 *
61 * @see #uncroppedSize()
62 */
63 private int uncroppedSize = -1;
64
65 /**
66 * Constructor for creating an empty {@link ScsiResponseDataSegment}.
67 *
68 * @see ScsiResponseDataSegment#EMPTY_DATA_SEGMENT
69 */
70 private ScsiResponseDataSegment () {
71 this(null, null, 0);
72 }
73
74 /**
75 * Creates a {@link ScsiResponseDataSegment} with {@link SenseData}.
76 *
77 * @param senseData the sense data sent as the result of SCSI error
78 * @param allocationLength number of bytes the initiator has allocated for data sent in the data segment
79 */
80 public ScsiResponseDataSegment (final SenseData senseData, final int allocationLength) {
81 this(senseData, null, allocationLength);
82 }
83
84 /**
85 * Creates a {@link ScsiResponseDataSegment} with {@link IResponseData}.
86 *
87 * @param responseData the data requested by the initiator
88 * @param allocationLength number of bytes the initiator has allocated for data sent in the data segment
89 */
90 public ScsiResponseDataSegment (final IResponseData responseData, final int allocationLength) {
91 this(null, responseData, allocationLength);
92 }
93
94 /**
95 * Creates a {@link ScsiResponseDataSegment} with both {@link SenseData} and {@link IResponseData}.
96 *
97 * @param senseData senseData the sense data sent as the result of SCSI error
98 * @param responseData data requested by the initiator
99 * @param allocationLength number of bytes the initiator has allocated for data sent in the data segment
100 */
101 private ScsiResponseDataSegment (final SenseData senseData, final IResponseData responseData, final int allocationLength) {
102 this.senseData = senseData;
103 this.responseData = responseData;
104 this.allocationLength = allocationLength;
105 }
106
107 /**
108 * Returns a {@link ByteBuffer} containing a serialized representation of this object.
109 *
110 * @return a {@link ByteBuffer} containing a serialized representation of this object
111 */
112 public ByteBuffer serialize () {
113
114 final int size = uncroppedSize();
115 if (size == 0) return ByteBuffer.allocate(0);// empty data segment
116
117 // calculate sense length
118 int senseLength;
119 if (senseData == null)
120 senseLength = 0;
121 else
122 senseLength = senseData.size();
123
124 // allocate buffer of appropriate size
125 final ByteBuffer buffer = ByteBuffer.allocate(size);
126
127 // write sense length field
128 ReadWrite.writeTwoByteInt(buffer,// buffer
129 senseLength,// value
130 0);// index
131
132 // write sense data
133 if (senseData != null) senseData.serialize(buffer, SENSE_LENGTH_FIELD_LENGTH);
134
135 // write and SCSI response data
136 if (responseData != null) responseData.serialize(buffer, SENSE_LENGTH_FIELD_LENGTH + senseLength);
137
138 if (allocationLength > 0 && buffer.capacity() > allocationLength) {
139 /*
140 * If the allocation length variable is valid and the response data segment is larger than the data-in
141 * buffer allocated by the initiator, limit the number of bytes returned.
142 */
143 buffer.position(0);
144 buffer.limit(allocationLength);
145 final ByteBuffer croppedBuffer = ByteBuffer.allocate(allocationLength);
146 croppedBuffer.put(buffer);
147 return croppedBuffer;
148 }
149
150 if (LOGGER.isDebugEnabled()) LOGGER.debug("SCSI Response Data Segment:\n" + Debug.byteBufferToString(buffer));
151
152 return buffer;
153 }
154
155 /**
156 * The length of the {@link ScsiResponseDataSegment}, without considering any limitations due to
157 * {@link #allocationLength}.
158 * <p>
159 * The returned value is calculated just once, and then stored in {@link #uncroppedSize}. This summation is
160 * performed only if {@link #uncroppedSize} is negative.
161 *
162 * @return length of the {@link ScsiResponseDataSegment}, without considering any limitations due to
163 * {@link #allocationLength}
164 */
165 public int uncroppedSize () {
166 if (uncroppedSize < 0) {
167 if (senseData == null && responseData == null) return 0;
168 int size = SENSE_LENGTH_FIELD_LENGTH;
169 if (senseData != null) size += senseData.size();
170 if (responseData != null) size += responseData.size();
171 uncroppedSize = size;
172 }
173 return uncroppedSize;
174 }
175
176 /**
177 * Indicates if any bytes have to be cropped during {@link #serialize()} calls. The method returns <code>true</code>
178 * if and only if the serialized length of this objects exceeds {@link #allocationLength}.
179 *
180 * @return <code>true</code> if and only if the serialized length of this objects exceeds {@link #allocationLength}
181 */
182 public boolean getResidualOverflow () {
183 if (uncroppedSize() > allocationLength) return true;
184 return false;
185 }
186
187 /**
188 * Returns the number of bytes that had to be cropped due to the total length of all contained fields exceeding the
189 * {@link #allocationLength}.
190 *
191 * @return the total number of bytes that have to be cropped during serialization
192 * @see #serialize()
193 */
194 public int getResidualCount () {
195 if (getResidualOverflow()) return uncroppedSize() - allocationLength;
196 return 0;
197 }
198 }