1 package org.jscsi.target.connection.stage.fullfeature;
2
3
4 import static org.jscsi.target.storage.IStorageModule.VIRTUAL_BLOCK_SIZE;
5
6 import java.io.IOException;
7 import java.security.DigestException;
8
9 import org.jscsi.exception.InternetSCSIException;
10 import org.jscsi.parser.AbstractMessageParser;
11 import org.jscsi.parser.BasicHeaderSegment;
12 import org.jscsi.parser.ProtocolDataUnit;
13 import org.jscsi.parser.data.DataOutParser;
14 import org.jscsi.parser.nop.NOPOutParser;
15 import org.jscsi.parser.scsi.SCSICommandParser;
16 import org.jscsi.parser.scsi.SCSIResponseParser;
17 import org.jscsi.parser.scsi.SCSIStatus;
18 import org.jscsi.target.TargetServer;
19 import org.jscsi.target.connection.TargetPduFactory;
20 import org.jscsi.target.connection.phase.TargetFullFeaturePhase;
21 import org.jscsi.target.scsi.ScsiResponseDataSegment;
22 import org.jscsi.target.scsi.cdb.ScsiOperationCode;
23 import org.jscsi.target.scsi.cdb.Write10Cdb;
24 import org.jscsi.target.scsi.cdb.Write6Cdb;
25 import org.jscsi.target.scsi.cdb.WriteCdb;
26 import org.jscsi.target.settings.SettingsException;
27 import org.jscsi.target.util.Debug;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31
32
33
34
35
36
37 public final class WriteStage extends ReadOrWriteStage {
38
39 private static final Logger LOGGER = LoggerFactory.getLogger(WriteStage.class);
40
41
42
43
44 private int expectedDataSequenceNumber = 0;
45
46 public WriteStage (TargetFullFeaturePhase targetFullFeaturePhase) {
47 super(targetFullFeaturePhase);
48 }
49
50
51
52
53
54
55
56
57
58 private void checkDataOutParser (final AbstractMessageParser parser) throws InternetSCSIException {
59 if (parser instanceof DataOutParser) {
60 final DataOutParser p = (DataOutParser) parser;
61 if (p.getDataSequenceNumber() != expectedDataSequenceNumber++) { throw new InternetSCSIException("received erroneous PDU in data-out sequence, expected " + (expectedDataSequenceNumber - 1)); }
62 } else if (parser instanceof NOPOutParser || parser instanceof SCSICommandParser) {
63
64 } else {
65 if (parser != null) {
66 throw new InternetSCSIException("received erroneous PDU in data-out sequence, " + parser.getClass().getName());
67 } else {
68 throw new InternetSCSIException("received erroneous PDU in data-out sequence, parser is null");
69 }
70 }
71
72 }
73
74 @Override
75 public void execute (ProtocolDataUnit pdu) throws IOException , DigestException , InterruptedException , InternetSCSIException , SettingsException {
76
77 if (LOGGER.isDebugEnabled()) LOGGER.debug("Entering WRITE STAGE");
78
79
80 final boolean immediateData = settings.getImmediateData();
81 final boolean initialR2T = settings.getInitialR2T();
82 final int firstBurstLength = settings.getFirstBurstLength();
83 final int maxBurstLength = settings.getMaxBurstLength();
84
85 if (LOGGER.isDebugEnabled()) {
86 LOGGER.debug("immediateData = " + immediateData);
87 LOGGER.debug("initialR2T = " + initialR2T);
88 }
89
90
91 BasicHeaderSegment bhs = pdu.getBasicHeaderSegment();
92 SCSICommandParser parser = (SCSICommandParser) bhs.getParser();
93 final int initiatorTaskTag = bhs.getInitiatorTaskTag();
94 WriteCdb cdb;
95 final ScsiOperationCode scsiOpCode = ScsiOperationCode.valueOf(parser.getCDB().get(0));
96 if (scsiOpCode == ScsiOperationCode.WRITE_10)
97 cdb = new Write10Cdb(parser.getCDB());
98 else if (scsiOpCode == ScsiOperationCode.WRITE_6)
99 cdb = new Write6Cdb(parser.getCDB());
100 else {
101
102
103 throw new InternetSCSIException("wrong SCSI Operation Code " + scsiOpCode + " in WriteStage");
104 }
105 final int transferLength = cdb.getTransferLength();
106 final long logicalBlockAddress = cdb.getLogicalBlockAddress();
107
108
109 final int transferLengthInBytes = transferLength * VIRTUAL_BLOCK_SIZE;
110 long storageIndex = logicalBlockAddress * VIRTUAL_BLOCK_SIZE;
111
112
113
114 checkOverAndUnderflow(cdb);
115
116 if (cdb.getIllegalFieldPointers() != null) {
117
118
119
120
121
122
123 LOGGER.debug("illegal field in Write CDB");
124 LOGGER.debug("CDB:\n" + Debug.byteBufferToString(parser.getCDB()));
125
126
127
128
129 final ProtocolDataUnit responsePdu = createFixedFormatErrorPdu(cdb.getIllegalFieldPointers(),
130 initiatorTaskTag, parser.getExpectedDataTransferLength());
131 connection.sendPdu(responsePdu);
132 return;
133 }
134
135
136 int bytesReceived = 0;
137
138
139 if (immediateData && bhs.getDataSegmentLength() > 0) {
140 final byte[] immediateDataArray = pdu.getDataSegment().array();
141
142 session.getStorageModule().write(immediateDataArray, storageIndex);
143 bytesReceived = immediateDataArray.length;
144
145 if (LOGGER.isDebugEnabled()) LOGGER.debug("wrote " + immediateDataArray.length + "bytes as immediate data");
146 }
147
148
149 if (!initialR2T && !bhs.isFinalFlag()) {
150
151 if (LOGGER.isDebugEnabled()) LOGGER.debug("receiving unsolicited data");
152
153 boolean firstBurstOver = false;
154 while (!firstBurstOver && bytesReceived <= firstBurstLength) {
155
156
157 pdu = connection.receivePdu();
158 bhs = pdu.getBasicHeaderSegment();
159
160 checkDataOutParser(bhs.getParser());
161
162 final DataOutParser dataOutParser = (DataOutParser) bhs.getParser();
163
164 session.getStorageModule().write(pdu.getDataSegment().array(), storageIndex + dataOutParser.getBufferOffset());
165 ;
166 bytesReceived += bhs.getDataSegmentLength();
167
168 if (bhs.isFinalFlag()) firstBurstOver = true;
169 }
170 }
171
172
173 if (bytesReceived < transferLengthInBytes) {
174 if (LOGGER.isDebugEnabled()) LOGGER.debug(bytesReceived + "<" + transferLengthInBytes);
175
176 int readyToTransferSequenceNumber = 0;
177 int desiredDataTransferLength;
178
179 while (bytesReceived < transferLengthInBytes) {
180
181 desiredDataTransferLength = Math.min(maxBurstLength, transferLengthInBytes - bytesReceived);
182
183
184 pdu = TargetPduFactory.createReadyToTransferPdu(0,
185 initiatorTaskTag, TargetServer.getNextTargetTransferTag(),
186 readyToTransferSequenceNumber++, bytesReceived,
187 desiredDataTransferLength);
188
189 connection.sendPdu(pdu);
190
191
192 expectedDataSequenceNumber = 0;
193
194 boolean solicitedDataCycleOver = false;
195 int bytesReceivedThisCycle = 0;
196 while (!solicitedDataCycleOver) {
197
198
199 pdu = connection.receivePdu();
200 bhs = pdu.getBasicHeaderSegment();
201 checkDataOutParser(bhs.getParser());
202
203 if (bhs.getParser() instanceof NOPOutParser) {
204
205
206 pdu = TargetPduFactory.createSCSIResponsePdu(false,
207 false,
208 false,
209 false,
210 SCSIResponseParser.ServiceResponse.COMMAND_COMPLETED_AT_TARGET,
211 SCSIStatus.GOOD,
212 initiatorTaskTag, 0,
213 0,
214 0,
215 0,
216 ScsiResponseDataSegment.EMPTY_DATA_SEGMENT);
217
218 connection.sendPdu(pdu);
219 return;
220 } else if (bhs.getParser() instanceof DataOutParser) {
221 final DataOutParser dataOutParser = (DataOutParser) bhs.getParser();
222
223 session.getStorageModule().write(pdu.getDataSegment().array(), storageIndex + dataOutParser.getBufferOffset());
224
225 bytesReceivedThisCycle += bhs.getDataSegmentLength();
226
227
228
229
230
231
232
233 if (bhs.isFinalFlag() || bytesReceivedThisCycle >= desiredDataTransferLength) solicitedDataCycleOver = true;
234 }
235 }
236 bytesReceived += bytesReceivedThisCycle;
237 }
238 }
239
240
241 pdu = TargetPduFactory.createSCSIResponsePdu(false,
242 false,
243 false,
244 false,
245 SCSIResponseParser.ServiceResponse.COMMAND_COMPLETED_AT_TARGET,
246 SCSIStatus.GOOD,
247 initiatorTaskTag, 0,
248 0,
249 0,
250 0,
251 ScsiResponseDataSegment.EMPTY_DATA_SEGMENT);
252
253 connection.sendPdu(pdu);
254 }
255 }