VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/ISCSIHDDCore.cpp@ 22264

Last change on this file since 22264 was 22258, checked in by vboxsync, 15 years ago

Storage/iSCSI: pick up data length parameters no matter where they show up

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 127.1 KB
Line 
1/** @file
2 * iSCSI initiator driver, VD backend.
3 */
4
5/*
6 * Copyright (C) 2006-2009 Sun Microsystems, Inc.
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 *
16 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
17 * Clara, CA 95054 USA or visit http://www.sun.com if you need
18 * additional information or have any questions.
19 */
20
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#define LOG_GROUP LOG_GROUP_VD_ISCSI
26#include "VBoxHDD-Internal.h"
27#define VBOX_VDICORE_VD /* Signal that the header is included from here. */
28#include "VDICore.h"
29#include <VBox/err.h>
30
31#include <VBox/log.h>
32#include <iprt/alloc.h>
33#include <iprt/assert.h>
34#include <iprt/uuid.h>
35#include <iprt/file.h>
36#include <iprt/string.h>
37#include <iprt/asm.h>
38#include <iprt/thread.h>
39#include <iprt/semaphore.h>
40#include <iprt/md5.h>
41#include <iprt/tcp.h>
42#include <iprt/time.h>
43#include <VBox/scsi.h>
44
45
46/*******************************************************************************
47* Defined Constants And Macros *
48*******************************************************************************/
49
50/** Default port number to use for iSCSI. */
51#define ISCSI_DEFAULT_PORT 3260
52
53
54/** Converts a number in the range of 0 - 15 into the corresponding hex char. */
55#define NUM_2_HEX(b) ('0' + (b) + (((b) > 9) ? 39 : 0))
56/** Converts a hex char into the corresponding number in the range 0-15. */
57#define HEX_2_NUM(c) (((c) <= '9') ? ((c) - '0') : (((c - 'A' + 10) & 0xf)))
58/* Converts a base64 char into the corresponding number in the range 0-63. */
59#define B64_2_NUM(c) ((c >= 'A' && c <= 'Z') ? (c - 'A') : (c >= 'a' && c <= 'z') ? (c - 'a' + 26) : (c >= '0' && c <= '9') ? (c - '0' + 52) : (c == '+') ? 62 : (c == '/') ? 63 : -1)
60
61
62/** Minumum CHAP_MD5 challenge length in bytes. */
63#define CHAP_MD5_CHALLENGE_MIN 16
64/** Maximum CHAP_MD5 challenge length in bytes. */
65#define CHAP_MD5_CHALLENGE_MAX 24
66
67
68/**
69 * SCSI peripheral device type. */
70typedef enum SCSIDEVTYPE
71{
72 /** direct-access device. */
73 SCSI_DEVTYPE_DISK = 0,
74 /** sequential-access device. */
75 SCSI_DEVTYPE_TAPE,
76 /** printer device. */
77 SCSI_DEVTYPE_PRINTER,
78 /** processor device. */
79 SCSI_DEVTYPE_PROCESSOR,
80 /** write-once device. */
81 SCSI_DEVTYPE_WORM,
82 /** CD/DVD device. */
83 SCSI_DEVTYPE_CDROM,
84 /** scanner device. */
85 SCSI_DEVTYPE_SCANNER,
86 /** optical memory device. */
87 SCSI_DEVTYPE_OPTICAL,
88 /** medium changer. */
89 SCSI_DEVTYPE_CHANGER,
90 /** communications device. */
91 SCSI_DEVTYPE_COMMUNICATION,
92 /** storage array controller device. */
93 SCSI_DEVTYPE_RAIDCTL = 0x0c,
94 /** enclosure services device. */
95 SCSI_DEVTYPE_ENCLOSURE,
96 /** simplified direct-access device. */
97 SCSI_DEVTYPE_SIMPLEDISK,
98 /** optical card reader/writer device. */
99 SCSI_DEVTYPE_OCRW,
100 /** bridge controller device. */
101 SCSI_DEVTYPE_BRIDGE,
102 /** object-based storage device. */
103 SCSI_DEVTYPE_OSD
104} SCSIDEVTYPE;
105
106/** Mask for extracting the SCSI device type out of the first byte of the INQUIRY response. */
107#define SCSI_DEVTYPE_MASK 0x1f
108
109
110/** Maximum PDU payload size we can handle in one piece. */
111#define ISCSI_DATA_LENGTH_MAX _256K
112
113/** Maximum PDU size we can handle in one piece. */
114#define ISCSI_RECV_PDU_BUFFER_SIZE (ISCSI_DATA_LENGTH_MAX + ISCSI_BHS_SIZE)
115
116
117/** Version of the iSCSI standard which this initiator driver can handle. */
118#define ISCSI_MY_VERSION 0
119
120
121/** Length of ISCSI basic header segment. */
122#define ISCSI_BHS_SIZE 48
123
124
125/** Reserved task tag value. */
126#define ISCSI_TASK_TAG_RSVD 0xffffffff
127
128
129/**
130 * iSCSI opcodes. */
131typedef enum ISCSIOPCODE
132{
133 /** NOP-Out. */
134 ISCSIOP_NOP_OUT = 0x00000000,
135 /** SCSI command. */
136 ISCSIOP_SCSI_CMD = 0x01000000,
137 /** SCSI task management request. */
138 ISCSIOP_SCSI_TASKMGMT_REQ = 0x02000000,
139 /** Login request. */
140 ISCSIOP_LOGIN_REQ = 0x03000000,
141 /** Text request. */
142 ISCSIOP_TEXT_REQ = 0x04000000,
143 /** SCSI Data-Out. */
144 ISCSIOP_SCSI_DATA_OUT = 0x05000000,
145 /** Logout request. */
146 ISCSIOP_LOGOUT_REQ = 0x06000000,
147 /** SNACK request. */
148 ISCSIOP_SNACK_REQ = 0x10000000,
149
150 /** NOP-In. */
151 ISCSIOP_NOP_IN = 0x20000000,
152 /** SCSI response. */
153 ISCSIOP_SCSI_RES = 0x21000000,
154 /** SCSI Task Management response. */
155 ISCSIOP_SCSI_TASKMGMT_RES = 0x22000000,
156 /** Login response. */
157 ISCSIOP_LOGIN_RES = 0x23000000,
158 /** Text response. */
159 ISCSIOP_TEXT_RES = 0x24000000,
160 /** SCSI Data-In. */
161 ISCSIOP_SCSI_DATA_IN = 0x25000000,
162 /** Logout response. */
163 ISCSIOP_LOGOUT_RES = 0x26000000,
164 /** Ready To Transfer (R2T). */
165 ISCSIOP_R2T = 0x31000000,
166 /** Asynchronous message. */
167 ISCSIOP_ASYN_MSG = 0x32000000,
168 /** Reject. */
169 ISCSIOP_REJECT = 0x3f000000
170} ISCSIOPCODE;
171
172/** Mask for extracting the iSCSI opcode out of the first header word. */
173#define ISCSIOP_MASK 0x3f000000
174
175
176/** ISCSI BHS word 0: Request should be processed immediately. */
177#define ISCSI_IMMEDIATE_DELIVERY_BIT 0x40000000
178
179/** ISCSI BHS word 0: This is the final PDU for this request/response. */
180#define ISCSI_FINAL_BIT 0x00800000
181/** ISCSI BHS word 0: Mask for extracting the CSG. */
182#define ISCSI_CSG_MASK 0x000c0000
183/** ISCSI BHS word 0: Shift offset for extracting the CSG. */
184#define ISCSI_CSG_SHIFT 18
185/** ISCSI BHS word 0: Mask for extracting the NSG. */
186#define ISCSI_NSG_MASK 0x00030000
187/** ISCSI BHS word 0: Shift offset for extracting the NSG. */
188#define ISCSI_NSG_SHIFT 16
189
190/** ISCSI BHS word 0: task attribute untagged */
191#define ISCSI_TASK_ATTR_UNTAGGED 0x00000000
192/** ISCSI BHS word 0: task attribute simple */
193#define ISCSI_TASK_ATTR_SIMPLE 0x00010000
194/** ISCSI BHS word 0: task attribute ordered */
195#define ISCSI_TASK_ATTR_ORDERED 0x00020000
196/** ISCSI BHS word 0: task attribute head of queue */
197#define ISCSI_TASK_ATTR_HOQ 0x00030000
198/** ISCSI BHS word 0: task attribute ACA */
199#define ISCSI_TASK_ATTR_ACA 0x00040000
200
201/** ISCSI BHS word 0: transit to next login phase. */
202#define ISCSI_TRANSIT_BIT 0x00800000
203/** ISCSI BHS word 0: continue with login negotiation. */
204#define ISCSI_CONTINUE_BIT 0x00400000
205
206/** ISCSI BHS word 0: residual underflow. */
207#define ISCSI_RESIDUAL_UNFL_BIT 0x00020000
208/** ISCSI BHS word 0: residual overflow. */
209#define ISCSI_RESIDUAL_OVFL_BIT 0x00040000
210/** ISCSI BHS word 0: Bidirectional read residual underflow. */
211#define ISCSI_BI_READ_RESIDUAL_UNFL_BIT 0x00080000
212/** ISCSI BHS word 0: Bidirectional read residual overflow. */
213#define ISCSI_BI_READ_RESIDUAL_OVFL_BIT 0x00100000
214
215/** ISCSI BHS word 0: SCSI response mask. */
216#define ISCSI_SCSI_RESPONSE_MASK 0x0000ff00
217/** ISCSI BHS word 0: SCSI status mask. */
218#define ISCSI_SCSI_STATUS_MASK 0x000000ff
219
220/** ISCSI BHS word 0: response includes status. */
221#define ISCSI_STATUS_BIT 0x00010000
222
223
224/**
225 * iSCSI login status class. */
226typedef enum ISCSILOGINSTATUSCLASS
227{
228 /** Success. */
229 ISCSI_LOGIN_STATUS_CLASS_SUCCESS = 0,
230 /** Redirection. */
231 ISCSI_LOGIN_STATUS_CLASS_REDIRECTION,
232 /** Initiator error. */
233 ISCSI_LOGIN_STATUS_CLASS_INITIATOR_ERROR,
234 /** Target error. */
235 ISCSI_LOGIN_STATUS_CLASS_TARGET_ERROR
236} ISCSILOGINSTATUSCLASS;
237
238
239/**
240 * iSCSI connection state. */
241typedef enum ISCSISTATE
242{
243 /** Not having a connection/session at all. */
244 ISCSISTATE_FREE,
245 /** Currently trying to login. */
246 ISCSISTATE_IN_LOGIN,
247 /** Normal operation, corresponds roughly to the Full Feature Phase. */
248 ISCSISTATE_NORMAL,
249 /** Currently trying to logout. */
250 ISCSISTATE_IN_LOGOUT
251} ISCSISTATE;
252
253
254/*******************************************************************************
255* Structures and Typedefs *
256*******************************************************************************/
257/**
258 * Block driver instance data.
259 */
260typedef struct ISCSIIMAGE
261{
262 /** Pointer to the filename (location). Not really used. */
263 const char *pszFilename;
264 /** Pointer to the initiator name. */
265 char *pszInitiatorName;
266 /** Pointer to the target name. */
267 char *pszTargetName;
268 /** Pointer to the target address. */
269 char *pszTargetAddress;
270 /** Pointer to the user name for authenticating the Initiator. */
271 char *pszInitiatorUsername;
272 /** Pointer to the secret for authenticating the Initiator. */
273 uint8_t *pbInitiatorSecret;
274 /** Length of the secret for authenticating the Initiator. */
275 size_t cbInitiatorSecret;
276 /** Pointer to the user name for authenticating the Target. */
277 char *pszTargetUsername;
278 /** Pointer to the secret for authenticating the Initiator. */
279 uint8_t *pbTargetSecret;
280 /** Length of the secret for authenticating the Initiator. */
281 size_t cbTargetSecret;
282 /** Initiator session identifier. */
283 uint64_t ISID;
284 /** SCSI Logical Unit Number. */
285 uint64_t LUN;
286 /** Pointer to the per-disk VD interface list. */
287 PVDINTERFACE pVDIfsDisk;
288 /** Error interface. */
289 PVDINTERFACE pInterfaceError;
290 /** Error interface callback table. */
291 PVDINTERFACEERROR pInterfaceErrorCallbacks;
292 /** TCP network stack interface. */
293 PVDINTERFACE pInterfaceNet;
294 /** TCP network stack interface callback table. */
295 PVDINTERFACETCPNET pInterfaceNetCallbacks;
296 /** Pointer to the per-image VD interface list. */
297 PVDINTERFACE pVDIfsImage;
298 /** Config interface. */
299 PVDINTERFACE pInterfaceConfig;
300 /** Config interface callback table. */
301 PVDINTERFACECONFIG pInterfaceConfigCallbacks;
302 /** Image open flags. */
303 unsigned uOpenFlags;
304 /** Number of re-login retries when a connection fails. */
305 uint32_t cISCSIRetries;
306 /** Size of volume in sectors. */
307 uint32_t cVolume;
308 /** Sector size on volume. */
309 uint32_t cbSector;
310 /** Total volume size in bytes. Easiert that multiplying the above values all the time. */
311 uint64_t cbSize;
312
313 /** Negotiated maximum data length when sending to target. */
314 uint32_t cbSendDataLength;
315 /** Negotiated maximum data length when receiving from target. */
316 uint32_t cbRecvDataLength;
317
318 /** Current state of the connection/session. */
319 ISCSISTATE state;
320 /** Flag whether the first Login Response PDU has been seen. */
321 bool FirstRecvPDU;
322 /** Initiator Task Tag of the last iSCSI request PDU. */
323 uint32_t ITT;
324 /** Sequence number of the last command. */
325 uint32_t CmdSN;
326 /** Sequence number of the next command expected by the target. */
327 uint32_t ExpCmdSN;
328 /** Maximum sequence number accepted by the target (determines size of window). */
329 uint32_t MaxCmdSN;
330 /** Expected sequence number of next status. */
331 uint32_t ExpStatSN;
332 /** Currently active request. */
333 PISCSIREQ paCurrReq;
334 /** Segment number of currently active request. */
335 uint32_t cnCurrReq;
336 /** Pointer to receive PDU buffer. (Freed by RT) */
337 void *pvRecvPDUBuf;
338 /** Length of receive PDU buffer. */
339 size_t cbRecvPDUBuf;
340 /** Mutex protecting against concurrent use from several threads. */
341 RTSEMMUTEX Mutex;
342
343 /** Pointer to the target hostname. */
344 char *pszHostname;
345 /** Pointer to the target hostname. */
346 uint32_t uPort;
347 /** Socket handle of the TCP connection. */
348 RTSOCKET Socket;
349 /** Timeout for read operations on the TCP connection (in milliseconds). */
350 uint32_t uReadTimeout;
351 /** Flag whether to use the host IP stack or DevINIP. */
352 bool fHostIP;
353} ISCSIIMAGE, *PISCSIIMAGE;
354
355
356/**
357 * SCSI transfer directions.
358 */
359typedef enum SCSIXFER
360{
361 SCSIXFER_NONE = 0,
362 SCSIXFER_TO_TARGET,
363 SCSIXFER_FROM_TARGET,
364 SCSIXFER_TO_FROM_TARGET
365} SCSIXFER, *PSCSIXFER;
366
367
368/**
369 * SCSI request structure.
370 */
371typedef struct SCSIREQ
372{
373 /** Transfer direction. */
374 SCSIXFER enmXfer;
375 /** Length of command block. */
376 size_t cbCmd;
377 /** Length of Initiator2Target data buffer. */
378 size_t cbI2TData;
379 /** Length of Target2Initiator data buffer. */
380 size_t cbT2IData;
381 /** Length of sense buffer. */
382 size_t cbSense;
383 /** Completion status of the command. */
384 uint8_t status;
385 /** Pointer to command block. */
386 void *pvCmd;
387 /** Pointer to Initiator2Target data buffer. */
388 const void *pcvI2TData;
389 /** Pointer to Target2Initiator data buffer. */
390 void *pvT2IData;
391 /** Pointer to sense buffer. */
392 void *pvSense;
393} SCSIREQ, *PSCSIREQ;
394
395
396/**
397 * iSCSI login negotiation parameter
398 */
399typedef struct ISCSIPARAMETER
400{
401 /** Name of the parameter. */
402 const char *pszParamName;
403 /** Value of the parameter. */
404 const char *pszParamValue;
405 /** Length of the binary parameter. 0=zero-terminated string. */
406 size_t cbParamValue;
407} ISCSIPARAMETER;
408
409
410/*******************************************************************************
411* Static Variables *
412*******************************************************************************/
413
414/** Counter for getting unique instance IDs. */
415static uint32_t s_u32iscsiID = 0;
416
417/** Default LUN. */
418static const char *s_iscsiConfigDefaultLUN = "0";
419
420/** Default initiator name. */
421static const char *s_iscsiConfigDefaultInitiatorName = "iqn.2009-08.com.sun.virtualbox.initiator";
422
423/** Default timeout, 10 seconds. */
424static const char *s_iscsiConfigDefaultTimeout = "10000";
425
426/** Default host IP stack. */
427static const char *s_iscsiConfigDefaultHostIPStack = "1";
428
429/** Description of all accepted config parameters. */
430static const VDCONFIGINFO s_iscsiConfigInfo[] =
431{
432 { "TargetName", NULL, VDCFGVALUETYPE_STRING, VD_CFGKEY_MANDATORY },
433 /* LUN is defined of string type to handle the "enc" prefix. */
434 { "LUN", s_iscsiConfigDefaultLUN, VDCFGVALUETYPE_STRING, VD_CFGKEY_MANDATORY },
435 { "TargetAddress", NULL, VDCFGVALUETYPE_STRING, VD_CFGKEY_MANDATORY },
436 { "InitiatorName", s_iscsiConfigDefaultInitiatorName, VDCFGVALUETYPE_STRING, 0 },
437 { "InitiatorUsername", NULL, VDCFGVALUETYPE_STRING, 0 },
438 { "InitiatorSecret", NULL, VDCFGVALUETYPE_BYTES, 0 },
439 { "TargetUsername", NULL, VDCFGVALUETYPE_STRING, VD_CFGKEY_EXPERT },
440 { "TargetSecret", NULL, VDCFGVALUETYPE_BYTES, VD_CFGKEY_EXPERT },
441 { "Timeout", s_iscsiConfigDefaultTimeout, VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
442 { "HostIPStack", s_iscsiConfigDefaultHostIPStack, VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
443 { NULL, NULL, VDCFGVALUETYPE_INTEGER, 0 }
444};
445
446/*******************************************************************************
447* Internal Functions *
448*******************************************************************************/
449
450/* iSCSI low-level functions (only to be used from the iSCSI high-level functions). */
451static uint32_t iscsiNewITT(PISCSIIMAGE pImage);
452static int iscsiSendPDU(PISCSIIMAGE pImage, PISCSIREQ paReq, uint32_t cnReq);
453static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes);
454static int drvISCSIValidatePDU(PISCSIRES paRes, uint32_t cnRes);
455static int iscsiTextAddKeyValue(uint8_t *pbBuf, size_t cbBuf, size_t *pcbBufCurr, const char *pcszKey, const char *pcszValue, size_t cbValue);
456static int iscsiTextGetKeyValue(const uint8_t *pbBuf, size_t cbBuf, const char *pcszKey, const char **ppcszValue);
457static int iscsiStrToBinary(const char *pcszValue, uint8_t *pbValue, size_t *pcbValue);
458static int iscsiUpdateParameters(PISCSIIMAGE pImage, const uint8_t *pbBuf, size_t cbBuf);
459
460/* Serial number arithmetic comparison. */
461static bool serial_number_less(uint32_t sn1, uint32_t sn2);
462
463/* CHAP-MD5 functions. */
464#ifdef IMPLEMENT_TARGET_AUTH
465static void chap_md5_generate_challenge(uint8_t *pbChallenge, size_t *pcbChallenge);
466#endif
467static void chap_md5_compute_response(uint8_t *pbResponse, uint8_t id, const uint8_t *pbChallenge, size_t cbChallenge,
468 const uint8_t *pbSecret, size_t cbSecret);
469
470
471/**
472 * Internal: signal an error to the frontend.
473 */
474DECLINLINE(int) iscsiError(PISCSIIMAGE pImage, int rc, RT_SRC_POS_DECL,
475 const char *pszFormat, ...)
476{
477 va_list va;
478 va_start(va, pszFormat);
479 if (pImage->pInterfaceError)
480 pImage->pInterfaceErrorCallbacks->pfnError(pImage->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
481 pszFormat, va);
482 va_end(va);
483 return rc;
484}
485
486
487static int iscsiTransportRead(PISCSIIMAGE pImage, PISCSIRES paResponse, unsigned int cnResponse)
488{
489 int rc = VINF_SUCCESS;
490 unsigned int i = 0;
491 size_t cbToRead, cbActuallyRead, residual, cbSegActual = 0, cbAHSLength, cbDataLength;
492 char *pDst;
493
494 LogFlowFunc(("cnResponse=%d (%s:%d)\n", cnResponse, pImage->pszHostname, pImage->uPort));
495 if (pImage->Socket == NIL_RTSOCKET)
496 {
497 /* Attempt to reconnect if the connection was previously broken. */
498 if (pImage->pszHostname != NULL)
499 {
500 rc = pImage->pInterfaceNetCallbacks->pfnClientConnect(pImage->pszHostname, pImage->uPort, &pImage->Socket);
501 if (RT_UNLIKELY( RT_FAILURE(rc)
502 && ( rc == VERR_NET_CONNECTION_REFUSED
503 || rc == VERR_NET_CONNECTION_RESET
504 || rc == VERR_NET_UNREACHABLE
505 || rc == VERR_NET_HOST_UNREACHABLE
506 || rc == VERR_NET_CONNECTION_TIMED_OUT)))
507 {
508 /* Standardize return value for no connection. */
509 rc = VERR_NET_CONNECTION_REFUSED;
510 }
511 }
512 }
513
514 if (RT_SUCCESS(rc) && paResponse[0].cbSeg >= 48)
515 {
516 cbToRead = 0;
517 residual = 48; /* Do not read more than the BHS length before the true PDU length is known. */
518 cbSegActual = residual;
519 pDst = (char *)paResponse[i].pvSeg;
520 uint64_t u64Timeout = RTTimeMilliTS() + pImage->uReadTimeout;
521 do
522 {
523 int64_t cMilliesRemaining = u64Timeout - RTTimeMilliTS();
524 if (cMilliesRemaining <= 0)
525 {
526 rc = VERR_TIMEOUT;
527 break;
528 }
529 Assert(cMilliesRemaining < 1000000);
530 rc = pImage->pInterfaceNetCallbacks->pfnSelectOne(pImage->Socket,
531 cMilliesRemaining);
532 if (RT_FAILURE(rc))
533 break;
534 rc = pImage->pInterfaceNetCallbacks->pfnRead(pImage->Socket,
535 pDst, residual,
536 &cbActuallyRead);
537 if (RT_FAILURE(rc))
538 break;
539 if (cbActuallyRead == 0)
540 {
541 /* The other end has closed the connection. */
542 pImage->pInterfaceNetCallbacks->pfnClientClose(pImage->Socket);
543 pImage->Socket = NIL_RTSOCKET;
544 rc = VERR_NET_CONNECTION_RESET;
545 break;
546 }
547 if (cbToRead == 0)
548 {
549 /* Currently reading the BHS. */
550 residual -= cbActuallyRead;
551 pDst += cbActuallyRead;
552 if (residual <= 40)
553 {
554 /* Enough data read to figure out the actual PDU size. */
555 uint32_t word1 = RT_N2H_U32(((uint32_t *)(paResponse[0].pvSeg))[1]);
556 cbAHSLength = (word1 & 0xff000000) >> 24;
557 cbAHSLength = ((cbAHSLength - 1) | 3) + 1; /* Add padding. */
558 cbDataLength = word1 & 0x00ffffff;
559 cbDataLength = ((cbDataLength - 1) | 3) + 1; /* Add padding. */
560 cbToRead = residual + cbAHSLength + cbDataLength;
561 residual += paResponse[0].cbSeg - 48;
562 if (residual > cbToRead)
563 residual = cbToRead;
564 cbSegActual = 48 + cbAHSLength + cbDataLength;
565 /* Check whether we are already done with this PDU (no payload). */
566 if (cbToRead == 0)
567 break;
568 }
569 }
570 else
571 {
572 cbToRead -= cbActuallyRead;
573 if (cbToRead == 0)
574 break;
575 pDst += cbActuallyRead;
576 residual -= cbActuallyRead;
577 }
578 if (residual == 0)
579 {
580 i++;
581 if (i >= cnResponse)
582 {
583 /* No space left in receive buffers. */
584 rc = VERR_BUFFER_OVERFLOW;
585 break;
586 }
587 pDst = (char *)paResponse[i].pvSeg;
588 residual = paResponse[i].cbSeg;
589 if (residual > cbToRead)
590 residual = cbToRead;
591 cbSegActual = residual;
592 }
593 } while (true);
594 }
595 else
596 {
597 if (RT_SUCCESS(rc))
598 rc = VERR_BUFFER_OVERFLOW;
599 }
600 if (RT_SUCCESS(rc))
601 {
602 paResponse[i].cbSeg = cbSegActual;
603 for (i++; i < cnResponse; i++)
604 paResponse[i].cbSeg = 0;
605 }
606
607 if (RT_UNLIKELY( RT_FAILURE(rc)
608 && ( rc == VERR_NET_CONNECTION_RESET
609 || rc == VERR_NET_CONNECTION_ABORTED
610 || rc == VERR_NET_CONNECTION_RESET_BY_PEER
611 || rc == VERR_NET_CONNECTION_REFUSED
612 || rc == VERR_BROKEN_PIPE)))
613 {
614 /* Standardize return value for broken connection. */
615 rc = VERR_BROKEN_PIPE;
616 }
617
618 LogFlowFunc(("returns %Rrc\n", rc));
619 return rc;
620}
621
622
623static int iscsiTransportWrite(PISCSIIMAGE pImage, PISCSIREQ paRequest, unsigned int cnRequest)
624{
625 int rc = VINF_SUCCESS;
626 uint32_t pad = 0;
627 unsigned int i;
628
629 LogFlow(("drvISCSITransportTcpWrite: cnRequest=%d (%s:%d)\n", cnRequest, pImage->pszHostname, pImage->uPort));
630 if (pImage->Socket == NIL_RTSOCKET)
631 {
632 /* Attempt to reconnect if the connection was previously broken. */
633 if (pImage->pszHostname != NULL)
634 {
635 rc = pImage->pInterfaceNetCallbacks->pfnClientConnect(pImage->pszHostname, pImage->uPort, &pImage->Socket);
636 if (RT_UNLIKELY( RT_FAILURE(rc)
637 && ( rc == VERR_NET_CONNECTION_REFUSED
638 || rc == VERR_NET_CONNECTION_RESET
639 || rc == VERR_NET_UNREACHABLE
640 || rc == VERR_NET_HOST_UNREACHABLE
641 || rc == VERR_NET_CONNECTION_TIMED_OUT)))
642 {
643 /* Standardize return value for no connection. */
644 rc = VERR_NET_CONNECTION_REFUSED;
645 }
646 }
647 }
648
649 if (RT_SUCCESS(rc))
650 {
651 for (i = 0; i < cnRequest; i++)
652 {
653 /* Write one chunk of data. */
654 rc = pImage->pInterfaceNetCallbacks->pfnWrite(pImage->Socket,
655 paRequest[i].pcvSeg,
656 paRequest[i].cbSeg);
657 if (RT_FAILURE(rc))
658 break;
659 /* Insert proper padding before the next chunk us written. */
660 if (paRequest[i].cbSeg & 3)
661 {
662 rc = pImage->pInterfaceNetCallbacks->pfnWrite(pImage->Socket,
663 &pad,
664 4 - (paRequest[i].cbSeg & 3));
665 if (RT_FAILURE(rc))
666 break;
667 }
668 }
669 /* Send out the request as soon as possible, otherwise the target will
670 * answer after an unnecessary delay. */
671 pImage->pInterfaceNetCallbacks->pfnFlush(pImage->Socket);
672 }
673
674 if (RT_UNLIKELY( RT_FAILURE(rc)
675 && ( rc == VERR_NET_CONNECTION_RESET
676 || rc == VERR_NET_CONNECTION_ABORTED
677 || rc == VERR_NET_CONNECTION_RESET_BY_PEER
678 || rc == VERR_NET_CONNECTION_REFUSED
679 || rc == VERR_BROKEN_PIPE)))
680 {
681 /* Standardize return value for broken connection. */
682 rc = VERR_BROKEN_PIPE;
683 }
684
685 LogFlow(("drvISCSITransportTcpWrite: returns %Rrc\n", rc));
686 return rc;
687}
688
689
690static int iscsiTransportOpen(PISCSIIMAGE pImage)
691{
692 int rc = VINF_SUCCESS;
693 size_t cbHostname = 0; /* shut up gcc */
694 const char *pcszPort = NULL; /* shut up gcc */
695 char *pszPortEnd;
696 uint16_t uPort;
697
698 /* Clean up previous connection data. */
699 if (pImage->Socket != NIL_RTSOCKET)
700 {
701 pImage->pInterfaceNetCallbacks->pfnClientClose(pImage->Socket);
702 pImage->Socket = NIL_RTSOCKET;
703 }
704 if (pImage->pszHostname)
705 {
706 RTMemFree(pImage->pszHostname);
707 pImage->pszHostname = NULL;
708 pImage->uPort = 0;
709 }
710
711 /* Locate the port number via the colon separating the hostname from the port. */
712 if (*pImage->pszTargetAddress)
713 {
714 if (*pImage->pszTargetAddress != '[')
715 {
716 /* Normal hostname or IPv4 dotted decimal. */
717 pcszPort = strchr(pImage->pszTargetAddress, ':');
718 if (pcszPort != NULL)
719 {
720 cbHostname = pcszPort - pImage->pszTargetAddress;
721 pcszPort++;
722 }
723 else
724 cbHostname = strlen(pImage->pszTargetAddress);
725 }
726 else
727 {
728 /* IPv6 literal address. Contains colons, so skip to closing square bracket. */
729 pcszPort = strchr(pImage->pszTargetAddress, ']');
730 if (pcszPort != NULL)
731 {
732 pcszPort++;
733 cbHostname = pcszPort - pImage->pszTargetAddress;
734 if (*pcszPort == '\0')
735 pcszPort = NULL;
736 else if (*pcszPort != ':')
737 rc = VERR_PARSE_ERROR;
738 else
739 pcszPort++;
740 }
741 else
742 rc = VERR_PARSE_ERROR;
743 }
744 }
745 else
746 rc = VERR_PARSE_ERROR;
747
748 /* Now split address into hostname and port. */
749 if (RT_SUCCESS(rc))
750 {
751 pImage->pszHostname = (char *)RTMemAlloc(cbHostname + 1);
752 if (!pImage->pszHostname)
753 rc = VERR_NO_MEMORY;
754 else
755 {
756 memcpy(pImage->pszHostname, pImage->pszTargetAddress, cbHostname);
757 pImage->pszHostname[cbHostname] = '\0';
758 if (pcszPort != NULL)
759 {
760 rc = RTStrToUInt16Ex(pcszPort, &pszPortEnd, 0, &uPort);
761 /* Note that RT_SUCCESS() macro to check the rc value is not strict enough in this case. */
762 if (rc == VINF_SUCCESS && *pszPortEnd == '\0' && uPort != 0)
763 {
764 pImage->uPort = uPort;
765 }
766 else
767 {
768 rc = VERR_PARSE_ERROR;
769 }
770 }
771 else
772 pImage->uPort = ISCSI_DEFAULT_PORT;
773 }
774 }
775
776 if (RT_FAILURE(rc))
777 {
778 if (pImage->pszHostname)
779 {
780 RTMemFree(pImage->pszHostname);
781 pImage->pszHostname = NULL;
782 }
783 pImage->uPort = 0;
784 }
785
786 /* Note that in this implementation the actual connection establishment is
787 * delayed until a PDU is read or written. */
788 LogFlowFunc(("returns %Rrc\n", rc));
789 return rc;
790}
791
792
793static int iscsiTransportClose(PISCSIIMAGE pImage)
794{
795 int rc;
796
797 LogFlowFunc(("(%s:%d)\n", pImage->pszHostname, pImage->uPort));
798 if (pImage->Socket != NIL_RTSOCKET)
799 {
800 rc = pImage->pInterfaceNetCallbacks->pfnClientClose(pImage->Socket);
801 pImage->Socket = NIL_RTSOCKET;
802 }
803 else
804 rc = VINF_SUCCESS;
805 LogFlowFunc(("returns %Rrc\n", rc));
806 return rc;
807}
808
809
810/**
811 * Attach to an iSCSI target. Performs all operations necessary to enter
812 * Full Feature Phase.
813 *
814 * @returns VBox status.
815 * @param pImage The iSCSI connection state to be used.
816 */
817static int iscsiAttach(PISCSIIMAGE pImage)
818{
819 int rc;
820 uint32_t itt;
821 uint32_t csg, nsg, substate;
822 uint64_t isid_tsih;
823 uint8_t bBuf[4096]; /* Should be large enough even for large authentication values. */
824 size_t cbBuf;
825 bool transit;
826 uint8_t pbChallenge[1024]; /* RFC3720 specifies this as maximum. */
827 size_t cbChallenge = 0; /* shut up gcc */
828 uint8_t bChapIdx;
829 uint8_t aResponse[RTMD5HASHSIZE];
830 uint32_t cnISCSIReq;
831 ISCSIREQ aISCSIReq[4];
832 uint32_t aReqBHS[12];
833 uint32_t cnISCSIRes;
834 ISCSIRES aISCSIRes[2];
835 uint32_t aResBHS[12];
836 char *pszNext;
837
838 bool fParameterNeg = true;;
839 pImage->cbRecvDataLength = ISCSI_DATA_LENGTH_MAX;
840 pImage->cbSendDataLength = ISCSI_DATA_LENGTH_MAX;
841 char szMaxDataLength[16];
842 RTStrPrintf(szMaxDataLength, sizeof(szMaxDataLength), "%u", ISCSI_DATA_LENGTH_MAX);
843 ISCSIPARAMETER aParameterNeg[] =
844 {
845 { "HeaderDigest", "None", 0 },
846 { "DataDigest", "None", 0 },
847 { "MaxConnections", "1", 0 },
848 { "InitialR2T", "No", 0 },
849 { "ImmediateData", "Yes", 0 },
850 { "MaxRecvDataSegmentLength", szMaxDataLength, 0 },
851 { "MaxBurstLength", szMaxDataLength, 0 },
852 { "FirstBurstLength", szMaxDataLength, 0 },
853 { "DefaultTime2Wait", "0", 0 },
854 { "DefaultTime2Retain", "60", 0 },
855 { "DataPDUInOrder", "Yes", 0 },
856 { "DataSequenceInOrder", "Yes", 0 },
857 { "ErrorRecoveryLevel", "0", 0 },
858 { "MaxOutstandingR2T", "1", 0 }
859 };
860
861 LogFlowFunc(("entering\n"));
862
863 Assert(pImage->state == ISCSISTATE_FREE);
864
865 RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
866
867 /* Make 100% sure the connection isn't reused for a new login. */
868 iscsiTransportClose(pImage);
869
870restart:
871 pImage->state = ISCSISTATE_IN_LOGIN;
872 pImage->ITT = 1;
873 pImage->FirstRecvPDU = true;
874 pImage->CmdSN = 1;
875 pImage->ExpCmdSN = 0;
876 pImage->MaxCmdSN = 1;
877 pImage->ExpStatSN = 1;
878
879 /*
880 * Send login request to target.
881 */
882 itt = iscsiNewITT(pImage);
883 csg = 0;
884 nsg = 0;
885 substate = 0;
886 isid_tsih = pImage->ISID << 16; /* TSIH field currently always 0 */
887
888 do {
889 transit = false;
890 cbBuf = 0;
891 /* Handle all cases with a single switch statement. */
892 switch (csg << 8 | substate)
893 {
894 case 0x0000: /* security negotiation, step 0: propose authentication. */
895 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "SessionType", "Normal", 0);
896 if (RT_FAILURE(rc))
897 goto out;
898 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "InitiatorName", pImage->pszInitiatorName, 0);
899 if (RT_FAILURE(rc))
900 goto out;
901 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "TargetName", pImage->pszTargetName, 0);
902 if (RT_FAILURE(rc))
903 goto out;
904 if (pImage->pszInitiatorUsername == NULL)
905 {
906 /* No authentication. Immediately switch to next phase. */
907 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "AuthMethod", "None", 0);
908 if (RT_FAILURE(rc))
909 goto out;
910 nsg = 1;
911 transit = true;
912 }
913 else
914 {
915 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "AuthMethod", "CHAP,None", 0);
916 if (RT_FAILURE(rc))
917 goto out;
918 }
919 break;
920 case 0x0001: /* security negotiation, step 1: propose CHAP_MD5 variant. */
921 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_A", "5", 0);
922 if (RT_FAILURE(rc))
923 goto out;
924 break;
925 case 0x0002: /* security negotiation, step 2: send authentication info. */
926 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_N", pImage->pszInitiatorUsername, 0);
927 if (RT_FAILURE(rc))
928 goto out;
929 chap_md5_compute_response(aResponse, bChapIdx, pbChallenge, cbChallenge,
930 pImage->pbInitiatorSecret, pImage->cbInitiatorSecret);
931 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_R", (const char *)aResponse, RTMD5HASHSIZE);
932 if (RT_FAILURE(rc))
933 goto out;
934 nsg = 1;
935 transit = true;
936 break;
937 case 0x0100: /* login operational negotiation, step 0: set parameters. */
938 if (fParameterNeg)
939 {
940 for (unsigned i = 0; i < RT_ELEMENTS(aParameterNeg); i++)
941 {
942 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf,
943 aParameterNeg[i].pszParamName,
944 aParameterNeg[i].pszParamValue,
945 aParameterNeg[i].cbParamValue);
946 if (RT_FAILURE(rc))
947 goto out;
948 }
949 fParameterNeg = false;
950 }
951
952 nsg = 3;
953 transit = true;
954 break;
955 case 0x0300: /* full feature phase. */
956 default:
957 /* Should never come here. */
958 AssertMsgFailed(("send: Undefined login state %d substate %d\n", csg, substate));
959 break;
960 }
961
962 aReqBHS[0] = RT_H2N_U32( ISCSI_IMMEDIATE_DELIVERY_BIT
963 | (csg << ISCSI_CSG_SHIFT)
964 | (transit ? (nsg << ISCSI_NSG_SHIFT | ISCSI_TRANSIT_BIT) : 0)
965 | ISCSI_MY_VERSION /* Minimum version. */
966 | (ISCSI_MY_VERSION << 8) /* Maximum version. */
967 | ISCSIOP_LOGIN_REQ); /* C=0 */
968 aReqBHS[1] = RT_H2N_U32((uint32_t)cbBuf); /* TotalAHSLength=0 */
969 aReqBHS[2] = RT_H2N_U32(isid_tsih >> 32);
970 aReqBHS[3] = RT_H2N_U32(isid_tsih & 0xffffffff);
971 aReqBHS[4] = itt;
972 aReqBHS[5] = RT_H2N_U32(1 << 16); /* CID=1,reserved */
973 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
974 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
975 aReqBHS[8] = 0; /* reserved */
976 aReqBHS[9] = 0; /* reserved */
977 aReqBHS[10] = 0; /* reserved */
978 aReqBHS[11] = 0; /* reserved */
979
980 cnISCSIReq = 0;
981 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
982 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
983 cnISCSIReq++;
984
985 aISCSIReq[cnISCSIReq].pcvSeg = bBuf;
986 aISCSIReq[cnISCSIReq].cbSeg = cbBuf;
987 cnISCSIReq++;
988
989 rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq);
990 if (RT_SUCCESS(rc))
991 {
992 ISCSIOPCODE cmd;
993 ISCSILOGINSTATUSCLASS loginStatusClass;
994
995 cnISCSIRes = 0;
996 aISCSIRes[cnISCSIRes].pvSeg = aResBHS;
997 aISCSIRes[cnISCSIRes].cbSeg = sizeof(aResBHS);
998 cnISCSIRes++;
999 aISCSIRes[cnISCSIRes].pvSeg = bBuf;
1000 aISCSIRes[cnISCSIRes].cbSeg = sizeof(bBuf);
1001 cnISCSIRes++;
1002
1003 rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes);
1004 if (RT_FAILURE(rc))
1005 break;
1006 /** @todo collect partial login responses with Continue bit set. */
1007 Assert(aISCSIRes[0].pvSeg == aResBHS);
1008 Assert(aISCSIRes[0].cbSeg >= ISCSI_BHS_SIZE);
1009 Assert((RT_N2H_U32(aResBHS[0]) & ISCSI_CONTINUE_BIT) == 0);
1010
1011 cmd = (ISCSIOPCODE)(RT_N2H_U32(aResBHS[0]) & ISCSIOP_MASK);
1012 if (cmd == ISCSIOP_LOGIN_RES)
1013 {
1014 if ((RT_N2H_U32(aResBHS[0]) & 0xff) != ISCSI_MY_VERSION)
1015 {
1016 iscsiTransportClose(pImage);
1017 rc = VERR_PARSE_ERROR;
1018 break; /* Give up immediately, as a RFC violation in version fields is very serious. */
1019 }
1020
1021 loginStatusClass = (ISCSILOGINSTATUSCLASS)(RT_N2H_U32(aResBHS[9]) >> 24);
1022 switch (loginStatusClass)
1023 {
1024 case ISCSI_LOGIN_STATUS_CLASS_SUCCESS:
1025 uint32_t targetCSG;
1026 uint32_t targetNSG;
1027 bool targetTransit;
1028
1029 if (pImage->FirstRecvPDU)
1030 {
1031 pImage->FirstRecvPDU = false;
1032 pImage->ExpStatSN = RT_N2H_U32(aResBHS[6]) + 1;
1033 }
1034
1035 targetCSG = (RT_N2H_U32(aResBHS[0]) & ISCSI_CSG_MASK) >> ISCSI_CSG_SHIFT;
1036 targetNSG = (RT_N2H_U32(aResBHS[0]) & ISCSI_NSG_MASK) >> ISCSI_NSG_SHIFT;
1037 targetTransit = !!(RT_N2H_U32(aResBHS[0]) & ISCSI_TRANSIT_BIT);
1038
1039 /* Handle all cases with a single switch statement. */
1040 switch (csg << 8 | substate)
1041 {
1042 case 0x0000: /* security negotiation, step 0: receive final authentication. */
1043 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1044 if (RT_FAILURE(rc))
1045 break;
1046
1047 const char *pcszAuthMethod;
1048
1049 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "AuthMethod", &pcszAuthMethod);
1050 if (RT_FAILURE(rc))
1051 {
1052 rc = VERR_PARSE_ERROR;
1053 break;
1054 }
1055 if (strcmp(pcszAuthMethod, "None") == 0)
1056 {
1057 /* Authentication offered, but none required. Skip to operational parameters. */
1058 csg = 1;
1059 nsg = 1;
1060 transit = true;
1061 substate = 0;
1062 break;
1063 }
1064 else if (strcmp(pcszAuthMethod, "CHAP") == 0 && targetNSG == 0 && !targetTransit)
1065 {
1066 /* CHAP authentication required, continue with next substate. */
1067 substate++;
1068 break;
1069 }
1070
1071 /* Unknown auth method or login response PDU headers incorrect. */
1072 rc = VERR_PARSE_ERROR;
1073 break;
1074 case 0x0001: /* security negotiation, step 1: receive final CHAP variant and challenge. */
1075 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1076 if (RT_FAILURE(rc))
1077 break;
1078
1079 const char *pcszChapAuthMethod;
1080 const char *pcszChapIdxTarget;
1081 const char *pcszChapChallengeStr;
1082
1083 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_A", &pcszChapAuthMethod);
1084 if (RT_FAILURE(rc))
1085 {
1086 rc = VERR_PARSE_ERROR;
1087 break;
1088 }
1089 if (strcmp(pcszChapAuthMethod, "5") != 0)
1090 {
1091 rc = VERR_PARSE_ERROR;
1092 break;
1093 }
1094 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_I", &pcszChapIdxTarget);
1095 if (RT_FAILURE(rc))
1096 {
1097 rc = VERR_PARSE_ERROR;
1098 break;
1099 }
1100 rc = RTStrToUInt8Ex(pcszChapIdxTarget, &pszNext, 0, &bChapIdx);
1101 if ((rc > VINF_SUCCESS) || *pszNext != '\0')
1102 {
1103 rc = VERR_PARSE_ERROR;
1104 break;
1105 }
1106 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_C", &pcszChapChallengeStr);
1107 if (RT_FAILURE(rc))
1108 {
1109 rc = VERR_PARSE_ERROR;
1110 break;
1111 }
1112 cbChallenge = sizeof(pbChallenge);
1113 rc = iscsiStrToBinary(pcszChapChallengeStr, pbChallenge, &cbChallenge);
1114 if (RT_FAILURE(rc))
1115 break;
1116 substate++;
1117 transit = true;
1118 break;
1119 case 0x0002: /* security negotiation, step 2: check authentication success. */
1120 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1121 if (RT_FAILURE(rc))
1122 break;
1123
1124 if (targetCSG == 0 && targetNSG == 1 && targetTransit)
1125 {
1126 /* Target wants to continue in login operational state, authentication success. */
1127 csg = 1;
1128 nsg = 3;
1129 substate = 0;
1130 break;
1131 }
1132 rc = VERR_PARSE_ERROR;
1133 break;
1134 case 0x0100: /* login operational negotiation, step 0: check results. */
1135 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1136 if (RT_FAILURE(rc))
1137 break;
1138
1139 if (targetCSG == 1 && targetNSG == 3 && targetTransit)
1140 {
1141 /* Target wants to continue in full feature phase, login finished. */
1142 csg = 3;
1143 nsg = 3;
1144 substate = 0;
1145 break;
1146 }
1147 else if (targetCSG == 1 && targetNSG == 1 && !targetTransit)
1148 {
1149 /* Target wants to negotiate certain parameters and
1150 * stay in login operational negotiation. */
1151 csg = 1;
1152 nsg = 3;
1153 substate = 0;
1154 }
1155 rc = VERR_PARSE_ERROR;
1156 break;
1157 case 0x0300: /* full feature phase. */
1158 default:
1159 AssertMsgFailed(("recv: Undefined login state %d substate %d\n", csg, substate));
1160 rc = VERR_PARSE_ERROR;
1161 break;
1162 }
1163 break;
1164 case ISCSI_LOGIN_STATUS_CLASS_REDIRECTION:
1165 const char *pcszTargetRedir;
1166
1167 /* Target has moved to some other location, as indicated in the TargetAddress key. */
1168 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "TargetAddress", &pcszTargetRedir);
1169 if (RT_FAILURE(rc))
1170 {
1171 rc = VERR_PARSE_ERROR;
1172 break;
1173 }
1174 if (pImage->pszTargetAddress)
1175 RTMemFree(pImage->pszTargetAddress);
1176 {
1177 size_t cb = strlen(pcszTargetRedir) + 1;
1178 pImage->pszTargetAddress = (char *)RTMemAlloc(cb);
1179 if (!pImage->pszTargetAddress)
1180 {
1181 rc = VERR_NO_MEMORY;
1182 break;
1183 }
1184 memcpy(pImage->pszTargetAddress, pcszTargetRedir, cb);
1185 }
1186 rc = iscsiTransportOpen(pImage);
1187 goto restart;
1188 case ISCSI_LOGIN_STATUS_CLASS_INITIATOR_ERROR:
1189 iscsiTransportClose(pImage);
1190 rc = VERR_IO_GEN_FAILURE;
1191 goto out;
1192 case ISCSI_LOGIN_STATUS_CLASS_TARGET_ERROR:
1193 iscsiTransportClose(pImage);
1194 rc = VINF_EOF;
1195 break;
1196 default:
1197 rc = VERR_PARSE_ERROR;
1198 }
1199
1200 if (csg == 3)
1201 {
1202 /*
1203 * Finished login, continuing with Full Feature Phase.
1204 */
1205 rc = VINF_SUCCESS;
1206 break;
1207 }
1208 }
1209 else
1210 {
1211 AssertMsgFailed(("%s: ignoring unexpected PDU with first word = %#08x\n", __FUNCTION__, RT_N2H_U32(aResBHS[0])));
1212 }
1213 }
1214 else
1215 break;
1216 } while (true);
1217
1218out:
1219 if (RT_FAILURE(rc))
1220 {
1221 /*
1222 * Close connection to target.
1223 */
1224 iscsiTransportClose(pImage);
1225 pImage->state = ISCSISTATE_FREE;
1226 }
1227 else
1228 pImage->state = ISCSISTATE_NORMAL;
1229
1230 RTSemMutexRelease(pImage->Mutex);
1231
1232 LogFlowFunc(("returning %Rrc\n", rc));
1233 LogRel(("iSCSI: login to target %s %s\n", pImage->pszTargetName, RT_SUCCESS(rc) ? "successful" : "failed"));
1234 return rc;
1235}
1236
1237
1238/**
1239 * Detach from an iSCSI target.
1240 *
1241 * @returns VBox status.
1242 * @param pImage The iSCSI connection state to be used.
1243 */
1244static int iscsiDetach(PISCSIIMAGE pImage)
1245{
1246 int rc;
1247 uint32_t itt;
1248 uint32_t cnISCSIReq = 0;
1249 ISCSIREQ aISCSIReq[4];
1250 uint32_t aReqBHS[12];
1251 LogFlow(("drvISCSIDetach: entering\n"));
1252
1253 RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
1254
1255 if (pImage->state != ISCSISTATE_FREE && pImage->state != ISCSISTATE_IN_LOGOUT)
1256 {
1257 pImage->state = ISCSISTATE_IN_LOGOUT;
1258
1259 /*
1260 * Send logout request to target.
1261 */
1262 itt = iscsiNewITT(pImage);
1263 aReqBHS[0] = RT_H2N_U32(ISCSI_FINAL_BIT | ISCSIOP_LOGOUT_REQ); /* I=0,F=1,Reason=close session */
1264 aReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
1265 aReqBHS[2] = 0; /* reserved */
1266 aReqBHS[3] = 0; /* reserved */
1267 aReqBHS[4] = itt;
1268 aReqBHS[5] = 0; /* reserved */
1269 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
1270 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
1271 aReqBHS[8] = 0; /* reserved */
1272 aReqBHS[9] = 0; /* reserved */
1273 aReqBHS[10] = 0; /* reserved */
1274 aReqBHS[11] = 0; /* reserved */
1275 pImage->CmdSN++;
1276
1277 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
1278 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
1279 cnISCSIReq++;
1280
1281 rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq);
1282 if (RT_SUCCESS(rc))
1283 {
1284 /*
1285 * Read logout response from target.
1286 */
1287 ISCSIRES aISCSIRes;
1288 uint32_t aResBHS[12];
1289
1290 aISCSIRes.pvSeg = aResBHS;
1291 aISCSIRes.cbSeg = sizeof(aResBHS);
1292 rc = iscsiRecvPDU(pImage, itt, &aISCSIRes, 1);
1293 if (RT_SUCCESS(rc))
1294 {
1295 if (RT_N2H_U32(aResBHS[0]) != (ISCSI_FINAL_BIT | ISCSIOP_LOGOUT_RES))
1296 AssertMsgFailed(("iSCSI Logout response invalid\n"));
1297 }
1298 else
1299 AssertMsgFailed(("iSCSI Logout response error, rc=%Rrc\n", rc));
1300 }
1301 else
1302 AssertMsgFailed(("Could not send iSCSI Logout request, rc=%Rrc\n", rc));
1303 }
1304
1305 if (pImage->state != ISCSISTATE_FREE)
1306 {
1307 /*
1308 * Close connection to target.
1309 */
1310 rc = iscsiTransportClose(pImage);
1311 if (RT_FAILURE(rc))
1312 AssertMsgFailed(("Could not close connection to target, rc=%Rrc\n", rc));
1313 }
1314
1315 pImage->state = ISCSISTATE_FREE;
1316
1317 RTSemMutexRelease(pImage->Mutex);
1318
1319 LogFlow(("drvISCSIDetach: leaving\n"));
1320 LogRel(("iSCSI: logout to target %s\n", pImage->pszTargetName));
1321 return VINF_SUCCESS;
1322}
1323
1324
1325/**
1326 * Perform a command on an iSCSI target. Target must be already in
1327 * Full Feature Phase.
1328 *
1329 * @returns VBOX status.
1330 * @param pImage The iSCSI connection state to be used.
1331 * @param pRequest Command descriptor. Contains all information about
1332 * the command, its transfer directions and pointers
1333 * to the buffer(s) used for transferring data and
1334 * status information.
1335 */
1336static int iscsiCommand(PISCSIIMAGE pImage, PSCSIREQ pRequest)
1337{
1338 int rc;
1339 uint32_t itt;
1340 uint32_t cbData;
1341 uint32_t cnISCSIReq = 0;
1342 ISCSIREQ aISCSIReq[4];
1343 uint32_t aReqBHS[12];
1344
1345 uint32_t *pDst = NULL;
1346 size_t cbBufLength;
1347 uint32_t aStat[64];
1348 uint32_t ExpDataSN = 0;
1349 bool final = false;
1350
1351 LogFlow(("iscsiCommand: entering, CmdSN=%d\n", pImage->CmdSN));
1352
1353 Assert(pRequest->enmXfer != SCSIXFER_TO_FROM_TARGET); /**< @todo not yet supported, would require AHS. */
1354 Assert(pRequest->cbI2TData <= 0xffffff); /* larger transfers would require R2T support. */
1355 Assert(pRequest->cbCmd <= 16); /* would cause buffer overrun below. */
1356
1357 /* If not in normal state, then the transport connection was dropped. Try
1358 * to reestablish by logging in, the target might be responsive again. */
1359 if (pImage->state == ISCSISTATE_FREE)
1360 rc = iscsiAttach(pImage);
1361
1362 /* If still not in normal state, then the underlying transport connection
1363 * cannot be established. Get out before bad things happen (and make
1364 * sure the caller suspends the VM again). */
1365 if (pImage->state != ISCSISTATE_NORMAL)
1366 {
1367 rc = VERR_NET_CONNECTION_REFUSED;
1368 goto out;
1369 }
1370
1371 /*
1372 * Send SCSI command to target with all I2T data included.
1373 */
1374 cbData = 0;
1375 if (pRequest->enmXfer == SCSIXFER_FROM_TARGET)
1376 cbData = (uint32_t)pRequest->cbT2IData;
1377 else
1378 cbData = (uint32_t)pRequest->cbI2TData;
1379
1380 RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
1381
1382 itt = iscsiNewITT(pImage);
1383 memset(aReqBHS, 0, sizeof(aReqBHS));
1384 aReqBHS[0] = RT_H2N_U32( ISCSI_FINAL_BIT | ISCSI_TASK_ATTR_ORDERED | ISCSIOP_SCSI_CMD
1385 | (pRequest->enmXfer << 21)); /* I=0,F=1,Attr=Ordered */
1386 aReqBHS[1] = RT_H2N_U32(0x00000000 | ((uint32_t)pRequest->cbI2TData & 0xffffff)); /* TotalAHSLength=0 */
1387 aReqBHS[2] = RT_H2N_U32(pImage->LUN >> 32);
1388 aReqBHS[3] = RT_H2N_U32(pImage->LUN & 0xffffffff);
1389 aReqBHS[4] = itt;
1390 aReqBHS[5] = RT_H2N_U32(cbData);
1391 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
1392 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
1393 memcpy(aReqBHS + 8, pRequest->pvCmd, pRequest->cbCmd);
1394 pImage->CmdSN++;
1395
1396 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
1397 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
1398 cnISCSIReq++;
1399
1400 if ( pRequest->enmXfer == SCSIXFER_TO_TARGET
1401 || pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET)
1402 {
1403 aISCSIReq[cnISCSIReq].pcvSeg = pRequest->pcvI2TData;
1404 aISCSIReq[cnISCSIReq].cbSeg = pRequest->cbI2TData; /* Padding done by transport. */
1405 cnISCSIReq++;
1406 }
1407
1408 rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq);
1409 if (RT_FAILURE(rc))
1410 goto out_release;
1411
1412 /* Place SCSI request in queue. */
1413 pImage->paCurrReq = aISCSIReq;
1414 pImage->cnCurrReq = cnISCSIReq;
1415
1416 /*
1417 * Read SCSI response/data in PDUs from target.
1418 */
1419 if ( pRequest->enmXfer == SCSIXFER_FROM_TARGET
1420 || pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET)
1421 {
1422 pDst = (uint32_t *)pRequest->pvT2IData;
1423 cbBufLength = pRequest->cbT2IData;
1424 }
1425 else
1426 cbBufLength = 0;
1427
1428 do {
1429 uint32_t cnISCSIRes = 0;
1430 ISCSIRES aISCSIRes[4];
1431 uint32_t aResBHS[12];
1432
1433 aISCSIRes[cnISCSIRes].pvSeg = aResBHS;
1434 aISCSIRes[cnISCSIRes].cbSeg = sizeof(aResBHS);
1435 cnISCSIRes++;
1436 if (cbBufLength != 0 &&
1437 ( pRequest->enmXfer == SCSIXFER_FROM_TARGET
1438 || pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET))
1439 {
1440 aISCSIRes[cnISCSIRes].pvSeg = pDst;
1441 aISCSIRes[cnISCSIRes].cbSeg = cbBufLength;
1442 cnISCSIRes++;
1443 }
1444 /* Always reserve space for the status - it's impossible to tell
1445 * beforehand whether this will be the final PDU or not. */
1446 aISCSIRes[cnISCSIRes].pvSeg = aStat;
1447 aISCSIRes[cnISCSIRes].cbSeg = sizeof(aStat);
1448 cnISCSIRes++;
1449
1450 rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes);
1451 if (RT_FAILURE(rc))
1452 break;
1453
1454 final = !!(RT_N2H_U32(aResBHS[0]) & ISCSI_FINAL_BIT);
1455 ISCSIOPCODE cmd = (ISCSIOPCODE)(RT_N2H_U32(aResBHS[0]) & ISCSIOP_MASK);
1456 if (cmd == ISCSIOP_SCSI_RES)
1457 {
1458 /* This is the final PDU which delivers the status (and may be omitted if
1459 * the last Data-In PDU included successful completion status). Note
1460 * that ExpStatSN has been bumped already in iscsiRecvPDU. */
1461 if (!final || ((RT_N2H_U32(aResBHS[0]) & 0x0000ff00) != 0) || (RT_N2H_U32(aResBHS[6]) != pImage->ExpStatSN - 1))
1462 {
1463 /* SCSI Response in the wrong place or with a (target) failure. */
1464 rc = VERR_PARSE_ERROR;
1465 break;
1466 }
1467 /* The following is a bit tricky, as in error situations we may
1468 * get the status only instead of the result data plus optional
1469 * status. Thus the status may have ended up partially in the
1470 * data area. */
1471 pRequest->status = RT_N2H_U32(aResBHS[0]) & 0x000000ff;
1472 uint32_t cbData = RT_N2H_U32(aResBHS[1]) & 0x00ffffff;
1473 if (cbData >= 2)
1474 {
1475 uint32_t cbStat = RT_N2H_U32(((uint32_t *)aISCSIRes[1].pvSeg)[0]) >> 16;
1476 if (cbStat + 2 > cbData || cbStat > pRequest->cbSense)
1477 {
1478 rc = VERR_BUFFER_OVERFLOW;
1479 break;
1480 }
1481 pRequest->cbSense = cbStat;
1482 memcpy(pRequest->pvSense, ((const uint8_t *)aISCSIRes[1].pvSeg) + 2, aISCSIRes[1].cbSeg - 2);
1483 if (cnISCSIRes > 2 && aISCSIRes[2].cbSeg && (ssize_t)cbStat - aISCSIRes[1].cbSeg - 2 > 0)
1484 memcpy((char *)pRequest->pvSense + aISCSIRes[1].cbSeg, aISCSIRes[2].pvSeg, cbStat - aISCSIRes[1].cbSeg - 2);
1485 }
1486 else if (cbData == 1)
1487 {
1488 rc = VERR_PARSE_ERROR;
1489 break;
1490 }
1491 else
1492 pRequest->cbSense = 0;
1493 break;
1494 }
1495 else if (cmd == ISCSIOP_SCSI_DATA_IN)
1496 {
1497 /* A Data-In PDU carries some data that needs to be added to the received
1498 * data in response to the command. There may be both partial and complete
1499 * Data-In PDUs, so collect data until the status is included or the status
1500 * is sent in a separate SCSI Result frame (see above). */
1501 if (final && aISCSIRes[2].cbSeg != 0)
1502 {
1503 /* The received PDU is partially stored in the buffer for status.
1504 * Must not happen under normal circumstances and is a target error. */
1505 rc = VERR_BUFFER_OVERFLOW;
1506 break;
1507 }
1508 uint32_t len = RT_N2H_U32(aResBHS[1]) & 0x00ffffff;
1509 pDst = (uint32_t *)((char *)pDst + len);
1510 cbBufLength -= len;
1511 ExpDataSN++;
1512 if (final && (RT_N2H_U32(aResBHS[0]) & ISCSI_STATUS_BIT) != 0)
1513 {
1514 pRequest->status = RT_N2H_U32(aResBHS[0]) & 0x000000ff;
1515 pRequest->cbSense = 0;
1516 break;
1517 }
1518 }
1519 else
1520 {
1521 rc = VERR_PARSE_ERROR;
1522 break;
1523 }
1524 } while (true);
1525
1526 /* Remove SCSI request from queue. */
1527 pImage->paCurrReq = NULL;
1528 pImage->cnCurrReq = 0;
1529
1530out_release:
1531 if (rc == VERR_TIMEOUT)
1532 {
1533 /* Drop connection in case the target plays dead. Much better than
1534 * delaying the next requests until the timed out command actually
1535 * finishes. Also keep in mind that command shouldn't take longer than
1536 * about 30-40 seconds, or the guest will lose its patience. */
1537 iscsiTransportClose(pImage);
1538 pImage->state = ISCSISTATE_FREE;
1539 }
1540 RTSemMutexRelease(pImage->Mutex);
1541
1542out:
1543 LogFlow(("iscsiCommand: returns %Rrc\n", rc));
1544 return rc;
1545}
1546
1547
1548/**
1549 * Generate a new Initiator Task Tag.
1550 *
1551 * @returns Initiator Task Tag.
1552 * @param pImage The iSCSI connection state to be used.
1553 */
1554static uint32_t iscsiNewITT(PISCSIIMAGE pImage)
1555{
1556 uint32_t next_itt;
1557
1558 next_itt = pImage->ITT++;
1559 if (pImage->ITT == ISCSI_TASK_TAG_RSVD)
1560 pImage->ITT = 0;
1561 return RT_H2N_U32(next_itt);
1562}
1563
1564
1565/**
1566 * Send an iSCSI request. The request can consist of several segments, which
1567 * are padded to 4 byte boundaries and concatenated.
1568 *
1569 * @returns VBOX status
1570 * @param pImage The iSCSI connection state to be used.
1571 * @param paReq Pointer to array of iSCSI request sections.
1572 * @param cnReq Number of valid iSCSI request sections in the array.
1573 */
1574static int iscsiSendPDU(PISCSIIMAGE pImage, PISCSIREQ paReq, uint32_t cnReq)
1575{
1576 int rc = VINF_SUCCESS;
1577 uint32_t i;
1578 /** @todo return VERR_VD_ISCSI_INVALID_STATE in the appropriate situations,
1579 * needs cleaning up of timeout/disconnect handling a bit, as otherwise
1580 * too many incorrect errors are signalled. */
1581 Assert(pImage->paCurrReq == NULL);
1582 Assert(cnReq >= 1);
1583 Assert(paReq[0].cbSeg >= ISCSI_BHS_SIZE);
1584
1585 for (i = 0; i < pImage->cISCSIRetries; i++)
1586 {
1587 rc = iscsiTransportWrite(pImage, paReq, cnReq);
1588 if (RT_SUCCESS(rc))
1589 break;
1590 if (rc != VERR_BROKEN_PIPE && rc != VERR_NET_CONNECTION_REFUSED)
1591 break;
1592 /* No point in reestablishing the connection for a logout */
1593 if (pImage->state == ISCSISTATE_IN_LOGOUT)
1594 break;
1595 RTThreadSleep(500);
1596 if (pImage->state != ISCSISTATE_IN_LOGIN)
1597 {
1598 /* Attempt to re-login when a connection fails, but only when not
1599 * currently logging in. */
1600 rc = iscsiAttach(pImage);
1601 if (RT_FAILURE(rc))
1602 break;
1603 }
1604 }
1605 return rc;
1606}
1607
1608
1609/**
1610 * Wait for an iSCSI response with a matching Initiator Target Tag. The response is
1611 * split into several segments, as requested by the caller-provided buffer specification.
1612 * Remember that the response can be split into several PDUs by the sender, so make
1613 * sure that all parts are collected and processed appropriately by the caller.
1614 *
1615 * @returns VBOX status
1616 * @param pImage The iSCSI connection state to be used.
1617 * @param paRes Pointer to array of iSCSI response sections.
1618 * @param cnRes Number of valid iSCSI response sections in the array.
1619 */
1620static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes)
1621{
1622 int rc = VINF_SUCCESS;
1623 uint32_t i;
1624 ISCSIRES aResBuf;
1625
1626 for (i = 0; i < pImage->cISCSIRetries; i++)
1627 {
1628 aResBuf.pvSeg = pImage->pvRecvPDUBuf;
1629 aResBuf.cbSeg = pImage->cbRecvPDUBuf;
1630 rc = iscsiTransportRead(pImage, &aResBuf, 1);
1631 if (RT_FAILURE(rc))
1632 {
1633 if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
1634 {
1635 /* No point in reestablishing the connection for a logout */
1636 if (pImage->state == ISCSISTATE_IN_LOGOUT)
1637 break;
1638 /* Connection broken while waiting for a response - wait a while and
1639 * try to restart by re-sending the original request (if any).
1640 * This also handles the connection reestablishment (login etc.). */
1641 RTThreadSleep(500);
1642 if (pImage->state != ISCSISTATE_IN_LOGIN)
1643 {
1644 /* Attempt to re-login when a connection fails, but only when not
1645 * currently logging in. */
1646 rc = iscsiAttach(pImage);
1647 if (RT_FAILURE(rc))
1648 break;
1649 }
1650 if (pImage->paCurrReq != NULL)
1651 {
1652 rc = iscsiSendPDU(pImage, pImage->paCurrReq, pImage->cnCurrReq);
1653 if (RT_FAILURE(rc))
1654 break;
1655 }
1656 }
1657 else
1658 {
1659 /* Signal other errors (VERR_BUFFER_OVERFLOW etc.) to the caller. */
1660 break;
1661 }
1662 }
1663 else
1664 {
1665 ISCSIOPCODE cmd;
1666 const uint32_t *pcvResSeg = (const uint32_t *)aResBuf.pvSeg;
1667
1668 /* Check whether the received PDU is valid, and update the internal state of
1669 * the iSCSI connection/session. */
1670 rc = drvISCSIValidatePDU(&aResBuf, 1);
1671 if (RT_FAILURE(rc))
1672 continue;
1673 cmd = (ISCSIOPCODE)(RT_N2H_U32(pcvResSeg[0]) & ISCSIOP_MASK);
1674 switch (cmd)
1675 {
1676 case ISCSIOP_SCSI_RES:
1677 case ISCSIOP_SCSI_TASKMGMT_RES:
1678 case ISCSIOP_SCSI_DATA_IN:
1679 case ISCSIOP_R2T:
1680 case ISCSIOP_ASYN_MSG:
1681 case ISCSIOP_TEXT_RES:
1682 case ISCSIOP_LOGIN_RES:
1683 case ISCSIOP_LOGOUT_RES:
1684 case ISCSIOP_REJECT:
1685 case ISCSIOP_NOP_IN:
1686 if (serial_number_less(pImage->MaxCmdSN, RT_N2H_U32(pcvResSeg[8])))
1687 pImage->MaxCmdSN = RT_N2H_U32(pcvResSeg[8]);
1688 if (serial_number_less(pImage->ExpCmdSN, RT_N2H_U32(pcvResSeg[7])))
1689 pImage->ExpCmdSN = RT_N2H_U32(pcvResSeg[7]);
1690 break;
1691 default:
1692 rc = VERR_PARSE_ERROR;
1693 }
1694 if (RT_FAILURE(rc))
1695 continue;
1696 if ( !pImage->FirstRecvPDU
1697 && (cmd != ISCSIOP_SCSI_DATA_IN || (RT_N2H_U32(pcvResSeg[0]) & ISCSI_STATUS_BIT)))
1698 {
1699 if (pImage->ExpStatSN == RT_N2H_U32(pcvResSeg[6]))
1700 {
1701 /* StatSN counter is not advanced on R2T and on a target SN update NOP-In. */
1702 if ( (cmd != ISCSIOP_R2T)
1703 && ((cmd != ISCSIOP_NOP_IN) || (RT_N2H_U32(pcvResSeg[4]) != ISCSI_TASK_TAG_RSVD)))
1704 pImage->ExpStatSN++;
1705 }
1706 else
1707 {
1708 rc = VERR_PARSE_ERROR;
1709 continue;
1710 }
1711 }
1712 /* Finally check whether the received PDU matches what the caller wants. */
1713 if (itt == pcvResSeg[4])
1714 {
1715 /* Copy received PDU (one segment) to caller-provided buffers. */
1716 uint32_t i;
1717 size_t cbSeg;
1718 const uint8_t *pSrc;
1719
1720 pSrc = (const uint8_t *)aResBuf.pvSeg;
1721 cbSeg = aResBuf.cbSeg;
1722 for (i = 0; i < cnRes; i++)
1723 {
1724 if (cbSeg > paRes[i].cbSeg)
1725 {
1726 memcpy(paRes[i].pvSeg, pSrc, paRes[i].cbSeg);
1727 pSrc += paRes[i].cbSeg;
1728 cbSeg -= paRes[i].cbSeg;
1729 }
1730 else
1731 {
1732 memcpy(paRes[i].pvSeg, pSrc, cbSeg);
1733 paRes[i].cbSeg = cbSeg;
1734 cbSeg = 0;
1735 break;
1736 }
1737 }
1738 if (cbSeg != 0)
1739 {
1740 rc = VERR_BUFFER_OVERFLOW;
1741 break;
1742 }
1743 for (i++; i < cnRes; i++)
1744 paRes[i].cbSeg = 0;
1745 break;
1746 }
1747 else if ( cmd == ISCSIOP_NOP_IN
1748 && RT_N2H_U32(pcvResSeg[5]) != ISCSI_TASK_TAG_RSVD)
1749 {
1750 const uint32_t *pcvResSeg = (const uint32_t *)aResBuf.pvSeg;
1751 uint32_t cnISCSIReq;
1752 ISCSIREQ aISCSIReq[4];
1753 uint32_t aReqBHS[12];
1754
1755 aReqBHS[0] = RT_H2N_U32(ISCSI_IMMEDIATE_DELIVERY_BIT | ISCSI_FINAL_BIT | ISCSIOP_NOP_OUT);
1756 aReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
1757 aReqBHS[2] = pcvResSeg[2]; /* copy LUN from NOP-In */
1758 aReqBHS[3] = pcvResSeg[3]; /* copy LUN from NOP-In */
1759 aReqBHS[4] = RT_H2N_U32(ISCSI_TASK_TAG_RSVD); /* ITT, reply */
1760 aReqBHS[5] = pcvResSeg[5]; /* copy TTT from NOP-In */
1761 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
1762 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
1763 aReqBHS[8] = 0; /* reserved */
1764 aReqBHS[9] = 0; /* reserved */
1765 aReqBHS[10] = 0; /* reserved */
1766 aReqBHS[11] = 0; /* reserved */
1767
1768 cnISCSIReq = 0;
1769 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
1770 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
1771 cnISCSIReq++;
1772
1773 iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq);
1774 }
1775 }
1776 }
1777 return rc;
1778}
1779
1780
1781/**
1782 * Check the static (not dependent on the connection/session state) validity of an iSCSI response PDU.
1783 *
1784 * @returns VBOX status
1785 * @param paRes Pointer to array of iSCSI response sections.
1786 * @param cnRes Number of valid iSCSI response sections in the array.
1787 */
1788static int drvISCSIValidatePDU(PISCSIRES paRes, uint32_t cnRes)
1789{
1790 const uint32_t *pcrgResBHS;
1791 uint32_t hw0;
1792 Assert(cnRes >= 1);
1793 Assert(paRes[0].cbSeg >= ISCSI_BHS_SIZE);
1794
1795 pcrgResBHS = (const uint32_t *)(paRes[0].pvSeg);
1796 hw0 = RT_N2H_U32(pcrgResBHS[0]);
1797 switch (hw0 & ISCSIOP_MASK)
1798 {
1799 case ISCSIOP_NOP_IN:
1800 /* NOP-In responses must not be split into several PDUs nor it may contain
1801 * ping data for target-initiated pings nor may both task tags be valid task tags. */
1802 if ( (hw0 & ISCSI_FINAL_BIT) == 0
1803 || ( RT_N2H_U32(pcrgResBHS[4]) == ISCSI_TASK_TAG_RSVD
1804 && RT_N2H_U32(pcrgResBHS[1]) != 0)
1805 || ( RT_N2H_U32(pcrgResBHS[4]) != ISCSI_TASK_TAG_RSVD
1806 && RT_N2H_U32(pcrgResBHS[5]) != ISCSI_TASK_TAG_RSVD))
1807 return VERR_PARSE_ERROR;
1808 break;
1809 case ISCSIOP_SCSI_RES:
1810 /* SCSI responses must not be split into several PDUs nor must the residual
1811 * bits be contradicting each other nor may the residual bits be set for PDUs
1812 * containing anything else but a completed command response. Underflow
1813 * is no reason for declaring a PDU as invalid, as the target may choose
1814 * to return less data than we assume to get. */
1815 if ( (hw0 & ISCSI_FINAL_BIT) == 0
1816 || ((hw0 & ISCSI_BI_READ_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_BI_READ_RESIDUAL_UNFL_BIT))
1817 || ((hw0 & ISCSI_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_RESIDUAL_UNFL_BIT))
1818 || ( ((hw0 & ISCSI_SCSI_RESPONSE_MASK) == 0)
1819 && ((hw0 & ISCSI_SCSI_STATUS_MASK) == SCSI_STATUS_OK)
1820 && (hw0 & ( ISCSI_BI_READ_RESIDUAL_OVFL_BIT | ISCSI_BI_READ_RESIDUAL_UNFL_BIT
1821 | ISCSI_RESIDUAL_OVFL_BIT))))
1822 return VERR_PARSE_ERROR;
1823 break;
1824 case ISCSIOP_LOGIN_RES:
1825 /* Login responses must not contain contradicting transit and continue bits. */
1826 if ((hw0 & ISCSI_CONTINUE_BIT) && ((hw0 & ISCSI_TRANSIT_BIT) != 0))
1827 return VERR_PARSE_ERROR;
1828 break;
1829 case ISCSIOP_TEXT_RES:
1830 /* Text responses must not contain contradicting final and continue bits nor
1831 * may the final bit be set for PDUs containing a target transfer tag other than
1832 * the reserved transfer tag (and vice versa). */
1833 if ( (((hw0 & ISCSI_CONTINUE_BIT) && (hw0 & ISCSI_FINAL_BIT) != 0))
1834 || (((hw0 & ISCSI_FINAL_BIT) && (RT_N2H_U32(pcrgResBHS[5]) != ISCSI_TASK_TAG_RSVD)))
1835 || (((hw0 & ISCSI_FINAL_BIT) == 0) && (RT_N2H_U32(pcrgResBHS[5]) == ISCSI_TASK_TAG_RSVD)))
1836 return VERR_PARSE_ERROR;
1837 break;
1838 case ISCSIOP_SCSI_DATA_IN:
1839 /* SCSI Data-in responses must not contain contradicting residual bits when
1840 * status bit is set. */
1841 if ((hw0 & ISCSI_STATUS_BIT) && (hw0 & ISCSI_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_RESIDUAL_UNFL_BIT))
1842 return VERR_PARSE_ERROR;
1843 break;
1844 case ISCSIOP_LOGOUT_RES:
1845 /* Logout responses must not have the final bit unset and may not contain any
1846 * data or additional header segments. */
1847 if ( ((hw0 & ISCSI_FINAL_BIT) == 0)
1848 || (RT_N2H_U32(pcrgResBHS[1]) != 0))
1849 return VERR_PARSE_ERROR;
1850 break;
1851 case ISCSIOP_ASYN_MSG:
1852 /* Asynchronous Messages must not have the final bit unser and may not contain
1853 * an initiator task tag. */
1854 if ( ((hw0 & ISCSI_FINAL_BIT) == 0)
1855 || (RT_N2H_U32(pcrgResBHS[4]) != ISCSI_TASK_TAG_RSVD))
1856 return VERR_PARSE_ERROR;
1857 break;
1858 case ISCSIOP_SCSI_TASKMGMT_RES:
1859 case ISCSIOP_R2T:
1860 case ISCSIOP_REJECT:
1861 default:
1862 /* Do some logging, ignore PDU. */
1863 LogFlow(("drvISCSIValidatePDU: ignore unhandled PDU, first word %#08x\n", RT_N2H_U32(pcrgResBHS[0])));
1864 return VERR_PARSE_ERROR;
1865 }
1866 /* A target must not send PDUs with MaxCmdSN less than ExpCmdSN-1. */
1867
1868 if (serial_number_less(RT_N2H_U32(pcrgResBHS[8]), RT_N2H_U32(pcrgResBHS[7])-1))
1869 return VERR_PARSE_ERROR;
1870
1871 return VINF_SUCCESS;
1872}
1873
1874
1875/**
1876 * Appends a key-value pair to the buffer. Normal ASCII strings (cbValue == 0) and large binary values
1877 * of a given length (cbValue > 0) are directly supported. Other value types must be converted to ASCII
1878 * by the caller. Strings must be in UTF-8 encoding.
1879 *
1880 * @returns VBOX status
1881 * @param pbBuf Pointer to the key-value buffer.
1882 * @param cbBuf Length of the key-value buffer.
1883 * @param pcbBufCurr Currently used portion of the key-value buffer.
1884 * @param pszKey Pointer to a string containing the key.
1885 * @param pszValue Pointer to either a string containing the value or to a large binary value.
1886 * @param cbValue Length of the binary value if applicable.
1887 */
1888static int iscsiTextAddKeyValue(uint8_t *pbBuf, size_t cbBuf, size_t *pcbBufCurr, const char *pcszKey,
1889 const char *pcszValue, size_t cbValue)
1890{
1891 size_t cbBufTmp = *pcbBufCurr;
1892 size_t cbKey = strlen(pcszKey);
1893 size_t cbValueEnc;
1894 uint8_t *pbCurr;
1895
1896 if (cbValue == 0)
1897 cbValueEnc = strlen(pcszValue);
1898 else
1899 cbValueEnc = cbValue * 2 + 2; /* 2 hex bytes per byte, 2 bytes prefix */
1900
1901 if (cbBuf < cbBufTmp + cbKey + 1 + cbValueEnc + 1)
1902 {
1903 /* Buffer would overflow, signal error. */
1904 return VERR_BUFFER_OVERFLOW;
1905 }
1906
1907 /*
1908 * Append a key=value pair (zero terminated string) to the end of the buffer.
1909 */
1910 pbCurr = pbBuf + cbBufTmp;
1911 memcpy(pbCurr, pcszKey, cbKey);
1912 pbCurr += cbKey;
1913 *pbCurr++ = '=';
1914 if (cbValue == 0)
1915 {
1916 memcpy(pbCurr, pcszValue, cbValueEnc);
1917 pbCurr += cbValueEnc;
1918 }
1919 else
1920 {
1921 *pbCurr++ = '0';
1922 *pbCurr++ = 'x';
1923 for (uint32_t i = 0; i < cbValue; i++)
1924 {
1925 uint8_t b;
1926 b = pcszValue[i];
1927 *pbCurr++ = NUM_2_HEX(b >> 4);
1928 *pbCurr++ = NUM_2_HEX(b & 0xf);
1929 }
1930 }
1931 *pbCurr = '\0';
1932 *pcbBufCurr = cbBufTmp + cbKey + 1 + cbValueEnc + 1;
1933
1934 return VINF_SUCCESS;
1935}
1936
1937
1938/**
1939 * Retrieve the value for a given key from the key=value buffer.
1940 *
1941 * @returns VBOX status.
1942 * @param pbBuf Buffer containing key=value pairs.
1943 * @param cbBuf Length of buffer with key=value pairs.
1944 * @param pszKey Pointer to key for which to retrieve the value.
1945 * @param ppszValue Pointer to value string pointer.
1946 */
1947static int iscsiTextGetKeyValue(const uint8_t *pbBuf, size_t cbBuf, const char *pcszKey, const char **ppcszValue)
1948{
1949 size_t cbKey = strlen(pcszKey);
1950
1951 while (cbBuf != 0)
1952 {
1953 size_t cbKeyValNull = strlen((const char *)pbBuf) + 1;
1954
1955 if (strncmp(pcszKey, (const char *)pbBuf, cbKey) == 0 && pbBuf[cbKey] == '=')
1956 {
1957 *ppcszValue = (const char *)(pbBuf + cbKey + 1);
1958 return VINF_SUCCESS;
1959 }
1960 pbBuf += cbKeyValNull;
1961 cbBuf -= cbKeyValNull;
1962 }
1963 return VERR_INVALID_NAME;
1964}
1965
1966
1967/**
1968 * Convert a long-binary value from a value string to the binary representation.
1969 *
1970 * @returns VBOX status
1971 * @param pszValue Pointer to a string containing the textual value representation.
1972 * @param pbValue Pointer to the value buffer for the binary value.
1973 * @param pcbValue In: length of value buffer, out: actual length of binary value.
1974 */
1975static int iscsiStrToBinary(const char *pcszValue, uint8_t *pbValue, size_t *pcbValue)
1976{
1977 size_t cbValue = *pcbValue;
1978 char c1, c2, c3, c4;
1979 Assert(cbValue >= 1);
1980
1981 if (strlen(pcszValue) < 3)
1982 return VERR_PARSE_ERROR;
1983 if (*pcszValue++ != '0')
1984 return VERR_PARSE_ERROR;
1985 switch (*pcszValue++)
1986 {
1987 case 'x':
1988 case 'X':
1989 if (strlen(pcszValue) & 1)
1990 {
1991 c1 = *pcszValue++;
1992 *pbValue++ = HEX_2_NUM(c1);
1993 cbValue--;
1994 }
1995 while (*pcszValue != '\0')
1996 {
1997 if (cbValue == 0)
1998 return VERR_BUFFER_OVERFLOW;
1999 c1 = *pcszValue++;
2000 if ((c1 < '0' || c1 > '9') && (c1 < 'a' || c1 > 'f') && (c1 < 'A' || c1 > 'F'))
2001 return VERR_PARSE_ERROR;
2002 c2 = *pcszValue++;
2003 if ((c2 < '0' || c2 > '9') && (c2 < 'a' || c2 > 'f') && (c2 < 'A' || c2 > 'F'))
2004 return VERR_PARSE_ERROR;
2005 *pbValue++ = (HEX_2_NUM(c1) << 4) | HEX_2_NUM(c2);
2006 cbValue--;
2007 }
2008 *pcbValue -= cbValue;
2009 break;
2010 case 'b':
2011 case 'B':
2012 if ((strlen(pcszValue) & 3) != 0)
2013 return VERR_PARSE_ERROR;
2014 while (*pcszValue != '\0')
2015 {
2016 uint32_t temp;
2017 if (cbValue == 0)
2018 return VERR_BUFFER_OVERFLOW;
2019 c1 = *pcszValue++;
2020 if ((c1 < 'A' || c1 > 'Z') && (c1 < 'a' || c1 >'z') && (c1 < '0' || c1 > '9') && (c1 != '+') && (c1 != '/'))
2021 return VERR_PARSE_ERROR;
2022 c2 = *pcszValue++;
2023 if ((c2 < 'A' || c2 > 'Z') && (c2 < 'a' || c2 >'z') && (c2 < '0' || c2 > '9') && (c2 != '+') && (c2 != '/'))
2024 return VERR_PARSE_ERROR;
2025 c3 = *pcszValue++;
2026 if ((c3 < 'A' || c3 > 'Z') && (c3 < 'a' || c3 >'z') && (c3 < '0' || c3 > '9') && (c3 != '+') && (c3 != '/') && (c3 != '='))
2027 return VERR_PARSE_ERROR;
2028 c4 = *pcszValue++;
2029 if ( (c3 == '=' && c4 != '=')
2030 || ((c4 < 'A' || c4 > 'Z') && (c4 < 'a' || c4 >'z') && (c4 < '0' || c4 > '9') && (c4 != '+') && (c4 != '/') && (c4 != '=')))
2031 return VERR_PARSE_ERROR;
2032 temp = (B64_2_NUM(c1) << 18) | (B64_2_NUM(c2) << 12);
2033 if (c3 == '=') {
2034 if (*pcszValue != '\0')
2035 return VERR_PARSE_ERROR;
2036 *pbValue++ = temp >> 16;
2037 cbValue--;
2038 } else {
2039 temp |= B64_2_NUM(c3) << 6;
2040 if (c4 == '=') {
2041 if (*pcszValue != '\0')
2042 return VERR_PARSE_ERROR;
2043 if (cbValue < 2)
2044 return VERR_BUFFER_OVERFLOW;
2045 *pbValue++ = temp >> 16;
2046 *pbValue++ = (temp >> 8) & 0xff;
2047 cbValue -= 2;
2048 }
2049 else
2050 {
2051 temp |= B64_2_NUM(c4);
2052 if (cbValue < 3)
2053 return VERR_BUFFER_OVERFLOW;
2054 *pbValue++ = temp >> 16;
2055 *pbValue++ = (temp >> 8) & 0xff;
2056 *pbValue++ = temp & 0xff;
2057 cbValue -= 3;
2058 }
2059 }
2060 }
2061 *pcbValue -= cbValue;
2062 break;
2063 default:
2064 return VERR_PARSE_ERROR;
2065 }
2066 return VINF_SUCCESS;
2067}
2068
2069
2070/**
2071 * Retrieve the relevant parameter values and update the initiator state.
2072 *
2073 * @returns VBOX status.
2074 * @param pImage Current iSCSI initiator state.
2075 * @param pbBuf Buffer containing key=value pairs.
2076 * @param cbBuf Length of buffer with key=value pairs.
2077 */
2078static int iscsiUpdateParameters(PISCSIIMAGE pImage, const uint8_t *pbBuf, size_t cbBuf)
2079{
2080 int rc;
2081 const char *pcszMaxRecvDataSegmentLength = NULL;
2082 const char *pcszMaxBurstLength = NULL;
2083 const char *pcszFirstBurstLength = NULL;
2084 rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "MaxRecvDataSegmentLength", &pcszMaxRecvDataSegmentLength);
2085 if (rc == VERR_INVALID_NAME)
2086 rc = VINF_SUCCESS;
2087 if (RT_FAILURE(rc))
2088 return VERR_PARSE_ERROR;
2089 rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "MaxBurstLength", &pcszMaxBurstLength);
2090 if (rc == VERR_INVALID_NAME)
2091 rc = VINF_SUCCESS;
2092 if (RT_FAILURE(rc))
2093 return VERR_PARSE_ERROR;
2094 rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "FirstBurstLength", &pcszFirstBurstLength);
2095 if (rc == VERR_INVALID_NAME)
2096 rc = VINF_SUCCESS;
2097 if (RT_FAILURE(rc))
2098 return VERR_PARSE_ERROR;
2099 if (pcszMaxRecvDataSegmentLength)
2100 {
2101 uint32_t cb = pImage->cbSendDataLength;
2102 rc = RTStrToUInt32Full(pcszMaxRecvDataSegmentLength, 0, &cb);
2103 AssertRC(rc);
2104 pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
2105 }
2106 if (pcszMaxBurstLength)
2107 {
2108 uint32_t cb = pImage->cbSendDataLength;
2109 rc = RTStrToUInt32Full(pcszMaxBurstLength, 0, &cb);
2110 AssertRC(rc);
2111 pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
2112 }
2113 if (pcszFirstBurstLength)
2114 {
2115 uint32_t cb = pImage->cbSendDataLength;
2116 rc = RTStrToUInt32Full(pcszFirstBurstLength, 0, &cb);
2117 AssertRC(rc);
2118 pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
2119 }
2120 return VINF_SUCCESS;
2121}
2122
2123
2124static bool serial_number_less(uint32_t s1, uint32_t s2)
2125{
2126 return (s1 < s2 && s2 - s1 < 0x80000000) || (s1 > s2 && s1 - s2 > 0x80000000);
2127}
2128
2129
2130#ifdef IMPLEMENT_TARGET_AUTH
2131static void chap_md5_generate_challenge(uint8_t *pbChallenge, size_t *pcbChallenge)
2132{
2133 uint8_t cbChallenge;
2134
2135 cbChallenge = RTrand_U8(CHAP_MD5_CHALLENGE_MIN, CHAP_MD5_CHALLENGE_MAX);
2136 RTrand_bytes(pbChallenge, cbChallenge);
2137 *pcbChallenge = cbChallenge;
2138}
2139#endif
2140
2141
2142static void chap_md5_compute_response(uint8_t *pbResponse, uint8_t id, const uint8_t *pbChallenge, size_t cbChallenge,
2143 const uint8_t *pbSecret, size_t cbSecret)
2144{
2145 RTMD5CONTEXT ctx;
2146 uint8_t bId;
2147
2148 bId = id;
2149 RTMd5Init(&ctx);
2150 RTMd5Update(&ctx, &bId, 1);
2151 RTMd5Update(&ctx, pbSecret, cbSecret);
2152 RTMd5Update(&ctx, pbChallenge, cbChallenge);
2153 RTMd5Final(pbResponse, &ctx);
2154}
2155
2156/**
2157 * Internal. Free all allocated space for representing an image, and optionally
2158 * delete the image from disk.
2159 */
2160static void iscsiFreeImage(PISCSIIMAGE pImage, bool fDelete)
2161{
2162 Assert(pImage);
2163 Assert(!fDelete); /* This MUST be false, the flag isn't supported. */
2164
2165 if (pImage->Mutex != NIL_RTSEMMUTEX)
2166 {
2167 /* Detaching only makes sense when the mutex is there. Otherwise the
2168 * failure happened long before we could attach to the target. */
2169 iscsiDetach(pImage);
2170 RTSemMutexDestroy(pImage->Mutex);
2171 pImage->Mutex = NIL_RTSEMMUTEX;
2172 }
2173 if (pImage->pszTargetName)
2174 {
2175 RTMemFree(pImage->pszTargetName);
2176 pImage->pszTargetName = NULL;
2177 }
2178 if (pImage->pszInitiatorName)
2179 {
2180 RTMemFree(pImage->pszInitiatorName);
2181 pImage->pszInitiatorName = NULL;
2182 }
2183 if (pImage->pszInitiatorUsername)
2184 {
2185 RTMemFree(pImage->pszInitiatorUsername);
2186 pImage->pszInitiatorUsername = NULL;
2187 }
2188 if (pImage->pbInitiatorSecret)
2189 {
2190 RTMemFree(pImage->pbInitiatorSecret);
2191 pImage->pbInitiatorSecret = NULL;
2192 }
2193 if (pImage->pszTargetUsername)
2194 {
2195 RTMemFree(pImage->pszTargetUsername);
2196 pImage->pszTargetUsername = NULL;
2197 }
2198 if (pImage->pbTargetSecret)
2199 {
2200 RTMemFree(pImage->pbTargetSecret);
2201 pImage->pbTargetSecret = NULL;
2202 }
2203 if (pImage->pvRecvPDUBuf)
2204 {
2205 RTMemFree(pImage->pvRecvPDUBuf);
2206 pImage->pvRecvPDUBuf = NULL;
2207 }
2208}
2209
2210/**
2211 * Internal: Open an image, constructing all necessary data structures.
2212 */
2213static int iscsiOpenImage(PISCSIIMAGE pImage, unsigned uOpenFlags)
2214{
2215 int rc;
2216 char *pszLUN = NULL, *pszLUNInitial = NULL;
2217 bool fLunEncoded = false;
2218 uint32_t uTimeoutDef = 0;
2219 uint64_t uHostIPTmp = 0;
2220 bool fHostIPDef = 0;
2221 rc = RTStrToUInt32Full(s_iscsiConfigDefaultTimeout, 0, &uTimeoutDef);
2222 AssertRC(rc);
2223 rc = RTStrToUInt64Full(s_iscsiConfigDefaultHostIPStack, 0, &uHostIPTmp);
2224 AssertRC(rc);
2225 fHostIPDef = !!uHostIPTmp;
2226
2227 pImage->uOpenFlags = uOpenFlags;
2228
2229 /* Get error signalling interface. */
2230 pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
2231 if (pImage->pInterfaceError)
2232 pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
2233
2234 /* Get TCP network stack interface. */
2235 pImage->pInterfaceNet = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_TCPNET);
2236 if (pImage->pInterfaceNet)
2237 pImage->pInterfaceNetCallbacks = VDGetInterfaceTcpNet(pImage->pInterfaceNet);
2238 else
2239 {
2240 rc = iscsiError(pImage, VERR_VD_ISCSI_UNKNOWN_INTERFACE,
2241 RT_SRC_POS, N_("iSCSI: TCP network stack interface missing"));
2242 goto out;
2243 }
2244
2245 /* Get configuration interface. */
2246 pImage->pInterfaceConfig = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_CONFIG);
2247 if (pImage->pInterfaceConfig)
2248 pImage->pInterfaceConfigCallbacks = VDGetInterfaceConfig(pImage->pInterfaceConfig);
2249 else
2250 {
2251 rc = iscsiError(pImage, VERR_VD_ISCSI_UNKNOWN_INTERFACE,
2252 RT_SRC_POS, N_("iSCSI: configuration interface missing"));
2253 goto out;
2254 }
2255
2256 pImage->ISID = 0x800000000000ULL | 0x001234560000ULL | (0x00000000cba0ULL + ASMAtomicIncU32(&s_u32iscsiID));
2257 pImage->cISCSIRetries = 10;
2258 pImage->state = ISCSISTATE_FREE;
2259 pImage->pvRecvPDUBuf = RTMemAlloc(ISCSI_RECV_PDU_BUFFER_SIZE);
2260 pImage->cbRecvPDUBuf = ISCSI_RECV_PDU_BUFFER_SIZE;
2261 if (pImage->pvRecvPDUBuf == NULL)
2262 {
2263 rc = VERR_NO_MEMORY;
2264 goto out;
2265 }
2266 pImage->Mutex = NIL_RTSEMMUTEX;
2267 rc = RTSemMutexCreate(&pImage->Mutex);
2268 if (RT_FAILURE(rc))
2269 goto out;
2270
2271 /* Validate configuration, detect unknown keys. */
2272 if (!VDCFGAreKeysValid(pImage->pInterfaceConfigCallbacks,
2273 pImage->pInterfaceConfig->pvUser,
2274 "TargetName\0InitiatorName\0LUN\0TargetAddress\0InitiatorUsername\0InitiatorSecret\0TargetUsername\0TargetSecret\0Timeout\0HostIPStack\0"))
2275 {
2276 rc = iscsiError(pImage, VERR_VD_ISCSI_UNKNOWN_CFG_VALUES, RT_SRC_POS, N_("iSCSI: configuration error: unknown configuration keys present"));
2277 goto out;
2278 }
2279
2280 /* Query the iSCSI upper level configuration. */
2281 rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
2282 pImage->pInterfaceConfig->pvUser,
2283 "TargetName", &pImage->pszTargetName);
2284 if (RT_FAILURE(rc))
2285 {
2286 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetName as string"));
2287 goto out;
2288 }
2289 rc = VDCFGQueryStringAllocDef(pImage->pInterfaceConfigCallbacks,
2290 pImage->pInterfaceConfig->pvUser,
2291 "InitiatorName", &pImage->pszInitiatorName,
2292 s_iscsiConfigDefaultInitiatorName);
2293 if (RT_FAILURE(rc))
2294 {
2295 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorName as string"));
2296 goto out;
2297 }
2298 rc = VDCFGQueryStringAllocDef(pImage->pInterfaceConfigCallbacks,
2299 pImage->pInterfaceConfig->pvUser,
2300 "LUN", &pszLUN, s_iscsiConfigDefaultLUN);
2301 if (RT_FAILURE(rc))
2302 {
2303 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read LUN as string"));
2304 goto out;
2305 }
2306 pszLUNInitial = pszLUN;
2307 if (!strncmp(pszLUN, "enc", 3))
2308 {
2309 fLunEncoded = true;
2310 pszLUN += 3;
2311 }
2312 rc = RTStrToUInt64Full(pszLUN, 0, &pImage->LUN);
2313 if (RT_FAILURE(rc))
2314 {
2315 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to convert LUN to integer"));
2316 goto out;
2317 }
2318 if (!fLunEncoded)
2319 {
2320 if (pImage->LUN <= 255)
2321 {
2322 pImage->LUN = pImage->LUN << 48; /* uses peripheral device addressing method */
2323 }
2324 else if (pImage->LUN <= 16383)
2325 {
2326 pImage->LUN = (pImage->LUN << 48) | RT_BIT_64(62); /* uses flat space addressing method */
2327 }
2328 else
2329 {
2330 rc = VERR_OUT_OF_RANGE;
2331 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: LUN number out of range (0-16383)"));
2332 goto out;
2333 }
2334 }
2335 rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
2336 pImage->pInterfaceConfig->pvUser,
2337 "TargetAddress", &pImage->pszTargetAddress);
2338 if (RT_FAILURE(rc))
2339 {
2340 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetAddress as string"));
2341 goto out;
2342 }
2343 pImage->pszInitiatorUsername = NULL;
2344 rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
2345 pImage->pInterfaceConfig->pvUser,
2346 "InitiatorUsername",
2347 &pImage->pszInitiatorUsername);
2348 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
2349 rc = VINF_SUCCESS;
2350 if (RT_FAILURE(rc))
2351 {
2352 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorUsername as string"));
2353 goto out;
2354 }
2355 pImage->pbInitiatorSecret = NULL;
2356 pImage->cbInitiatorSecret = 0;
2357 rc = VDCFGQueryBytesAlloc(pImage->pInterfaceConfigCallbacks,
2358 pImage->pInterfaceConfig->pvUser,
2359 "InitiatorSecret",
2360 (void **)&pImage->pbInitiatorSecret,
2361 &pImage->cbInitiatorSecret);
2362 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
2363 rc = VINF_SUCCESS;
2364 if (RT_FAILURE(rc))
2365 {
2366 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorSecret as byte string"));
2367 goto out;
2368 }
2369 pImage->pszTargetUsername = NULL;
2370 rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
2371 pImage->pInterfaceConfig->pvUser,
2372 "TargetUsername",
2373 &pImage->pszTargetUsername);
2374 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
2375 rc = VINF_SUCCESS;
2376 if (RT_FAILURE(rc))
2377 {
2378 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetUsername as string"));
2379 goto out;
2380 }
2381 pImage->pbTargetSecret = NULL;
2382 pImage->cbTargetSecret = 0;
2383 rc = VDCFGQueryBytesAlloc(pImage->pInterfaceConfigCallbacks,
2384 pImage->pInterfaceConfig->pvUser,
2385 "TargetSecret", (void **)&pImage->pbTargetSecret,
2386 &pImage->cbTargetSecret);
2387 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
2388 rc = VINF_SUCCESS;
2389 if (RT_FAILURE(rc))
2390 {
2391 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetSecret as byte string"));
2392 goto out;
2393 }
2394
2395 pImage->pszHostname = NULL;
2396 pImage->uPort = 0;
2397 pImage->Socket = NIL_RTSOCKET;
2398 /* Query the iSCSI lower level configuration. */
2399 rc = VDCFGQueryU32Def(pImage->pInterfaceConfigCallbacks,
2400 pImage->pInterfaceConfig->pvUser,
2401 "Timeout", &pImage->uReadTimeout,
2402 uTimeoutDef);
2403 if (RT_FAILURE(rc))
2404 {
2405 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read Timeout as U32"));
2406 goto out;
2407 }
2408 rc = VDCFGQueryBoolDef(pImage->pInterfaceConfigCallbacks,
2409 pImage->pInterfaceConfig->pvUser,
2410 "HostIPStack", &pImage->fHostIP,
2411 fHostIPDef);
2412 if (RT_FAILURE(rc))
2413 {
2414 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read HostIPStack as boolean"));
2415 goto out;
2416 }
2417
2418 /* Don't actually establish iSCSI transport connection if this is just an
2419 * open to query the image information and the host IP stack isn't used.
2420 * Even trying is rather useless, as in this context the InTnet IP stack
2421 * isn't present. Returning dummies is the best possible result anyway. */
2422 if ((uOpenFlags & VD_OPEN_FLAGS_INFO) && !pImage->fHostIP)
2423 {
2424 LogFunc(("Not opening the transport connection as IntNet IP stack is not available. Will return dummies\n"));
2425 goto out;
2426 }
2427
2428 /*
2429 * Establish the iSCSI transport connection.
2430 */
2431 rc = iscsiTransportOpen(pImage);
2432 if (RT_SUCCESS(rc))
2433 rc = iscsiAttach(pImage);
2434
2435 if (RT_FAILURE(rc))
2436 {
2437 LogRel(("iSCSI: could not open target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
2438 goto out;
2439 }
2440 LogFlowFunc(("target '%s' opened successfully\n", pImage->pszTargetName));
2441
2442 SCSIREQ sr;
2443 uint8_t sense[32];
2444 uint8_t data8[8];
2445
2446 /*
2447 * Inquire available LUNs - purely dummy request.
2448 */
2449 uint8_t cdb_rlun[12];
2450 uint8_t rlundata[16];
2451 cdb_rlun[0] = SCSI_REPORT_LUNS;
2452 cdb_rlun[1] = 0; /* reserved */
2453 cdb_rlun[2] = 0; /* reserved */
2454 cdb_rlun[3] = 0; /* reserved */
2455 cdb_rlun[4] = 0; /* reserved */
2456 cdb_rlun[5] = 0; /* reserved */
2457 cdb_rlun[6] = sizeof(rlundata) >> 24;
2458 cdb_rlun[7] = (sizeof(rlundata) >> 16) & 0xff;
2459 cdb_rlun[8] = (sizeof(rlundata) >> 8) & 0xff;
2460 cdb_rlun[9] = sizeof(rlundata) & 0xff;
2461 cdb_rlun[10] = 0; /* reserved */
2462 cdb_rlun[11] = 0; /* control */
2463
2464 sr.enmXfer = SCSIXFER_FROM_TARGET;
2465 sr.cbCmd = sizeof(cdb_rlun);
2466 sr.pvCmd = cdb_rlun;
2467 sr.cbI2TData = 0;
2468 sr.pcvI2TData = NULL;
2469 sr.cbT2IData = sizeof(rlundata);
2470 sr.pvT2IData = rlundata;
2471 sr.cbSense = sizeof(sense);
2472 sr.pvSense = sense;
2473
2474 rc = iscsiCommand(pImage, &sr);
2475 if (RT_FAILURE(rc))
2476 {
2477 LogRel(("iSCSI: Could not get LUN info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
2478 return rc;
2479 }
2480
2481 /*
2482 * Inquire device characteristics - no tapes, scanners etc., please.
2483 */
2484 uint8_t cdb_inq[6];
2485 cdb_inq[0] = SCSI_INQUIRY;
2486 cdb_inq[1] = 0; /* reserved */
2487 cdb_inq[2] = 0; /* reserved */
2488 cdb_inq[3] = 0; /* reserved */
2489 cdb_inq[4] = sizeof(data8);
2490 cdb_inq[5] = 0; /* control */
2491
2492 sr.enmXfer = SCSIXFER_FROM_TARGET;
2493 sr.cbCmd = sizeof(cdb_inq);
2494 sr.pvCmd = cdb_inq;
2495 sr.cbI2TData = 0;
2496 sr.pcvI2TData = NULL;
2497 sr.cbT2IData = sizeof(data8);
2498 sr.pvT2IData = data8;
2499 sr.cbSense = sizeof(sense);
2500 sr.pvSense = sense;
2501
2502 rc = iscsiCommand(pImage, &sr);
2503 if (RT_SUCCESS(rc))
2504 {
2505 if ((data8[0] & SCSI_DEVTYPE_MASK) != SCSI_DEVTYPE_DISK)
2506 {
2507 rc = iscsiError(pImage, VERR_VD_ISCSI_INVALID_TYPE,
2508 RT_SRC_POS, N_("iSCSI: target address %s, target name %s, SCSI LUN %lld reports device type=%u"),
2509 pImage->pszTargetAddress, pImage->pszTargetName,
2510 pImage->LUN, data8[0]);
2511 LogRel(("iSCSI: Unsupported SCSI peripheral device type %d for target %s\n", data8[0] & SCSI_DEVTYPE_MASK, pImage->pszTargetName));
2512 goto out;
2513 }
2514 }
2515 else
2516 {
2517 LogRel(("iSCSI: Could not get INQUIRY info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
2518 goto out;
2519 }
2520
2521 /*
2522 * Query write disable bit in the device specific parameter entry in the
2523 * mode parameter header. Refuse read/write opening of read only disks.
2524 */
2525
2526 uint8_t cdb_ms[6];
2527 uint8_t data4[4];
2528 cdb_ms[0] = SCSI_MODE_SENSE_6;
2529 cdb_ms[1] = 0; /* dbd=0/reserved */
2530 cdb_ms[2] = 0x3f; /* pc=0/page code=0x3f, ask for all pages */
2531 cdb_ms[3] = 0; /* subpage code=0, return everything in page_0 format */
2532 cdb_ms[4] = sizeof(data4); /* allocation length=4 */
2533 cdb_ms[5] = 0; /* control */
2534
2535 sr.enmXfer = SCSIXFER_FROM_TARGET;
2536 sr.cbCmd = sizeof(cdb_ms);
2537 sr.pvCmd = cdb_ms;
2538 sr.cbI2TData = 0;
2539 sr.pcvI2TData = NULL;
2540 sr.cbT2IData = sizeof(data4);
2541 sr.pvT2IData = data4;
2542 sr.cbSense = sizeof(sense);
2543 sr.pvSense = sense;
2544
2545 rc = iscsiCommand(pImage, &sr);
2546 if (RT_SUCCESS(rc))
2547 {
2548 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY) && data4[2] & 0x80)
2549 {
2550 rc = VERR_VD_IMAGE_READ_ONLY;
2551 goto out;
2552 }
2553 }
2554 else
2555 {
2556 LogRel(("iSCSI: Could not get MODE SENSE info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
2557 goto out;
2558 }
2559
2560 /*
2561 * Determine sector size and capacity of the volume immediately.
2562 */
2563 uint8_t cdb_cap[10];
2564
2565 cdb_cap[0] = SCSI_READ_CAPACITY;
2566 cdb_cap[1] = 0; /* reserved */
2567 cdb_cap[2] = 0; /* reserved */
2568 cdb_cap[3] = 0; /* reserved */
2569 cdb_cap[4] = 0; /* reserved */
2570 cdb_cap[5] = 0; /* reserved */
2571 cdb_cap[6] = 0; /* reserved */
2572 cdb_cap[7] = 0; /* reserved */
2573 cdb_cap[8] = 0; /* reserved */
2574 cdb_cap[9] = 0; /* control */
2575
2576 sr.enmXfer = SCSIXFER_FROM_TARGET;
2577 sr.cbCmd = sizeof(cdb_cap);
2578 sr.pvCmd = cdb_cap;
2579 sr.cbI2TData = 0;
2580 sr.pcvI2TData = NULL;
2581 sr.cbT2IData = sizeof(data8);
2582 sr.pvT2IData = data8;
2583 sr.cbSense = sizeof(sense);
2584 sr.pvSense = sense;
2585
2586 rc = iscsiCommand(pImage, &sr);
2587 if (RT_SUCCESS(rc))
2588 {
2589 pImage->cVolume = (data8[0] << 24) | (data8[1] << 16) | (data8[2] << 8) | data8[3];
2590 pImage->cVolume++;
2591 pImage->cbSector = (data8[4] << 24) | (data8[5] << 16) | (data8[6] << 8) | data8[7];
2592 pImage->cbSize = (uint64_t)(pImage->cVolume) * pImage->cbSector;
2593 if (pImage->cVolume == 0 || pImage->cbSector == 0)
2594 {
2595 rc = iscsiError(pImage, VERR_VD_ISCSI_INVALID_TYPE,
2596 RT_SRC_POS, N_("iSCSI: target address %s, target name %s, SCSI LUN %lld reports media sector count=%lu sector size=%lu"),
2597 pImage->pszTargetAddress, pImage->pszTargetName,
2598 pImage->LUN, pImage->cVolume, pImage->cbSector);
2599 }
2600 }
2601 else
2602 {
2603 LogRel(("iSCSI: Could not determine capacity of target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
2604 goto out;
2605 }
2606
2607 /*
2608 * Check the read and write cache bits.
2609 * Try to enable the cache if it is disabled.
2610 *
2611 * We already checked that this is a block access device. No need
2612 * to do it again.
2613 */
2614 uint8_t aCachingModePage[32];
2615 uint8_t aCDBModeSense6[6];
2616
2617 memset(aCachingModePage, '\0', sizeof(aCachingModePage));
2618 aCDBModeSense6[0] = SCSI_MODE_SENSE_6;
2619 aCDBModeSense6[1] = 0;
2620 aCDBModeSense6[2] = (0x00 << 6) | (0x08 & 0x3f); /* Current values and caching mode page */
2621 aCDBModeSense6[3] = 0; /* Sub page code. */
2622 aCDBModeSense6[4] = sizeof(aCachingModePage) & 0xff;
2623 aCDBModeSense6[5] = 0;
2624 sr.enmXfer = SCSIXFER_FROM_TARGET;
2625 sr.cbCmd = sizeof(aCDBModeSense6);
2626 sr.pvCmd = aCDBModeSense6;
2627 sr.cbI2TData = 0;
2628 sr.pcvI2TData = NULL;
2629 sr.cbT2IData = sizeof(aCachingModePage);
2630 sr.pvT2IData = aCachingModePage;
2631 sr.cbSense = sizeof(sense);
2632 sr.pvSense = sense;
2633 rc = iscsiCommand(pImage, &sr);
2634 if ( RT_SUCCESS(rc)
2635 && (sr.status == SCSI_STATUS_OK)
2636 && (aCachingModePage[0] >= 15)
2637 && (aCachingModePage[4 + aCachingModePage[3]] & 0x3f) == 0x08
2638 && (aCachingModePage[4 + aCachingModePage[3] + 1] > 3))
2639 {
2640 uint32_t Offset = 4 + aCachingModePage[3];
2641 /*
2642 * Check if the read and/or the write cache is disabled.
2643 * The write cache is disabled if bit 2 (WCE) is zero and
2644 * the read cache is disabled if bit 0 (RCD) is set.
2645 */
2646 if (!ASMBitTest(&aCachingModePage[Offset + 2], 2) || ASMBitTest(&aCachingModePage[Offset + 2], 0))
2647 {
2648 /*
2649 * Write Cache Enable (WCE) bit is zero or the Read Cache Disable (RCD) is one
2650 * So one of the caches is disabled. Enable both caches.
2651 * The rest is unchanged.
2652 */
2653 ASMBitSet(&aCachingModePage[Offset + 2], 2);
2654 ASMBitClear(&aCachingModePage[Offset + 2], 0);
2655
2656 uint8_t aCDBCaching[6];
2657 aCDBCaching[0] = SCSI_MODE_SELECT_6;
2658 aCDBCaching[1] = 0; /* Don't write the page into NV RAM. */
2659 aCDBCaching[2] = 0;
2660 aCDBCaching[3] = 0;
2661 aCDBCaching[4] = sizeof(aCachingModePage) & 0xff;
2662 aCDBCaching[5] = 0;
2663 sr.enmXfer = SCSIXFER_TO_TARGET;
2664 sr.cbCmd = sizeof(aCDBCaching);
2665 sr.pvCmd = aCDBCaching;
2666 sr.cbI2TData = sizeof(aCachingModePage);
2667 sr.pcvI2TData = aCachingModePage;
2668 sr.cbT2IData = 0;
2669 sr.pvT2IData = NULL;
2670 sr.cbSense = sizeof(sense);
2671 sr.pvSense = sense;
2672 sr.status = 0;
2673 rc = iscsiCommand(pImage, &sr);
2674 if ( RT_SUCCESS(rc)
2675 && (sr.status == SCSI_STATUS_OK))
2676 {
2677 LogRel(("iSCSI: Enabled read and write cache of target %s\n", pImage->pszTargetName));
2678 }
2679 else
2680 {
2681 /* Log failures but continue. */
2682 LogRel(("iSCSI: Could not enable read and write cache of target %s, rc=%Rrc status=%#x\n",
2683 pImage->pszTargetName, rc, sr.status));
2684 LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sense));
2685 rc = VINF_SUCCESS;
2686 }
2687 }
2688 }
2689 else
2690 {
2691 /* Log errors but continue. */
2692 LogRel(("iSCSI: Could not check write cache of target %s, rc=%Rrc, got mode page %#x\n", pImage->pszTargetName, rc,aCachingModePage[0] & 0x3f));
2693 LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sense));
2694 rc = VINF_SUCCESS;
2695 }
2696
2697
2698out:
2699 if (RT_FAILURE(rc))
2700 iscsiFreeImage(pImage, false);
2701 return rc;
2702}
2703
2704
2705/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
2706static int iscsiCheckIfValid(const char *pszFilename)
2707{
2708 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
2709
2710 /* iSCSI images can't be checked for validity this way, as the filename
2711 * just can't supply enough configuration information. */
2712 int rc = VERR_VD_ISCSI_INVALID_HEADER;
2713
2714 LogFlowFunc(("returns %Rrc\n", rc));
2715 return rc;
2716}
2717
2718
2719/** @copydoc VBOXHDDBACKEND::pfnOpen */
2720static int iscsiOpen(const char *pszFilename, unsigned uOpenFlags,
2721 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
2722 void **ppBackendData)
2723{
2724 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
2725 int rc;
2726 PISCSIIMAGE pImage;
2727
2728 /* Check open flags. All valid flags are supported. */
2729 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
2730 {
2731 rc = VERR_INVALID_PARAMETER;
2732 goto out;
2733 }
2734
2735 /* Check remaining arguments. */
2736 if ( !VALID_PTR(pszFilename)
2737 || !*pszFilename
2738 || strchr(pszFilename, '"'))
2739 {
2740 rc = VERR_INVALID_PARAMETER;
2741 goto out;
2742 }
2743
2744 pImage = (PISCSIIMAGE)RTMemAllocZ(sizeof(ISCSIIMAGE));
2745 if (!pImage)
2746 {
2747 rc = VERR_NO_MEMORY;
2748 goto out;
2749 }
2750
2751 pImage->pszFilename = pszFilename;
2752 pImage->pszInitiatorName = NULL;
2753 pImage->pszTargetName = NULL;
2754 pImage->pszTargetAddress = NULL;
2755 pImage->pszInitiatorUsername = NULL;
2756 pImage->pbInitiatorSecret = NULL;
2757 pImage->pszTargetUsername = NULL;
2758 pImage->pbTargetSecret = NULL;
2759 pImage->paCurrReq = NULL;
2760 pImage->pvRecvPDUBuf = NULL;
2761 pImage->pszHostname = NULL;
2762 pImage->pVDIfsDisk = pVDIfsDisk;
2763 pImage->pVDIfsImage = pVDIfsImage;
2764
2765 rc = iscsiOpenImage(pImage, uOpenFlags);
2766 if (RT_SUCCESS(rc))
2767 *ppBackendData = pImage;
2768
2769out:
2770 if (RT_SUCCESS(rc))
2771 {
2772 LogFlowFunc(("target %s cVolume %d, cbSector %d\n", pImage->pszTargetName, pImage->cVolume, pImage->cbSector));
2773 LogRel(("iSCSI: target address %s, target name %s, SCSI LUN %lld\n", pImage->pszTargetAddress, pImage->pszTargetName, pImage->LUN));
2774 }
2775 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
2776 return rc;
2777}
2778
2779
2780/** @copydoc VBOXHDDBACKEND::pfnCreate */
2781static int iscsiCreate(const char *pszFilename, uint64_t cbSize,
2782 unsigned uImageFlags, const char *pszComment,
2783 PCPDMMEDIAGEOMETRY pPCHSGeometry,
2784 PCPDMMEDIAGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
2785 unsigned uOpenFlags, unsigned uPercentStart,
2786 unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
2787 PVDINTERFACE pVDIfsImage, PVDINTERFACE pVDIfsOperation,
2788 void **ppBackendData)
2789{
2790 LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p", pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
2791 int rc = VERR_NOT_SUPPORTED;
2792
2793 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
2794 return rc;
2795}
2796
2797
2798/** @copydoc VBOXHDDBACKEND::pfnRename */
2799static int iscsiRename(void *pBackendData, const char *pszFilename)
2800{
2801 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
2802 int rc = VERR_NOT_SUPPORTED;
2803
2804 LogFlowFunc(("returns %Rrc\n", rc));
2805 return rc;
2806}
2807
2808
2809/** @copydoc VBOXHDDBACKEND::pfnClose */
2810static int iscsiClose(void *pBackendData, bool fDelete)
2811{
2812 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
2813 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
2814 int rc = VINF_SUCCESS;
2815
2816 Assert(!fDelete); /* This flag is unsupported. */
2817
2818 /* Freeing a never allocated image (e.g. because the open failed) is
2819 * not signalled as an error. After all nothing bad happens. */
2820 if (pImage)
2821 iscsiFreeImage(pImage, fDelete);
2822
2823 LogFlowFunc(("returns %Rrc\n", rc));
2824 return rc;
2825}
2826
2827
2828/** @copydoc VBOXHDDBACKEND::pfnRead */
2829static int iscsiRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
2830 size_t cbToRead, size_t *pcbActuallyRead)
2831{
2832 /** @todo reinstate logging of the target everywhere - dropped temporarily */
2833 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
2834 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
2835 uint64_t lba;
2836 uint16_t tls;
2837 int rc;
2838
2839 Assert(pImage);
2840 Assert(uOffset % 512 == 0);
2841 Assert(cbToRead % 512 == 0);
2842
2843 Assert(pImage->cbSector);
2844 AssertPtr(pvBuf);
2845
2846 if ( uOffset + cbToRead > pImage->cbSize
2847 || cbToRead == 0)
2848 {
2849 rc = VERR_INVALID_PARAMETER;
2850 goto out;
2851 }
2852
2853 /*
2854 * Clip read size to a value which is supported by the target.
2855 */
2856 cbToRead = RT_MIN(cbToRead, pImage->cbRecvDataLength);
2857
2858 lba = uOffset / pImage->cbSector;
2859 tls = (uint16_t)(cbToRead / pImage->cbSector);
2860 SCSIREQ sr;
2861 uint8_t cdb[10];
2862 uint8_t sense[32];
2863
2864 cdb[0] = SCSI_READ_10;
2865 cdb[1] = 0; /* reserved */
2866 cdb[2] = (lba >> 24) & 0xff;
2867 cdb[3] = (lba >> 16) & 0xff;
2868 cdb[4] = (lba >> 8) & 0xff;
2869 cdb[5] = lba & 0xff;
2870 cdb[6] = 0; /* reserved */
2871 cdb[7] = (tls >> 8) & 0xff;
2872 cdb[8] = tls & 0xff;
2873 cdb[9] = 0; /* control */
2874
2875 sr.enmXfer = SCSIXFER_FROM_TARGET;
2876 sr.cbCmd = sizeof(cdb);
2877 sr.pvCmd = cdb;
2878 sr.cbI2TData = 0;
2879 sr.pcvI2TData = NULL;
2880 sr.cbT2IData = cbToRead;
2881 sr.pvT2IData = pvBuf;
2882 sr.cbSense = sizeof(sense);
2883 sr.pvSense = sense;
2884
2885 for (unsigned i = 0; i < 10; i++)
2886 {
2887 rc = iscsiCommand(pImage, &sr);
2888 if ( (RT_SUCCESS(rc) && !sr.cbSense)
2889 || RT_FAILURE(rc))
2890 break;
2891 rc = VERR_READ_ERROR;
2892 }
2893 if (RT_FAILURE(rc))
2894 {
2895 AssertMsgFailed(("iscsiCommand(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
2896 *pcbActuallyRead = 0;
2897 }
2898 else
2899 *pcbActuallyRead = sr.cbT2IData;
2900
2901out:
2902 LogFlowFunc(("returns %Rrc\n", rc));
2903 return rc;
2904}
2905
2906
2907/** @copydoc VBOXHDDBACKEND::pfnWrite */
2908static int iscsiWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
2909 size_t cbToWrite, size_t *pcbWriteProcess,
2910 size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
2911{
2912 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
2913 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
2914 uint64_t lba;
2915 uint16_t tls;
2916 int rc;
2917
2918 Assert(pImage);
2919 Assert(uOffset % 512 == 0);
2920 Assert(cbToWrite % 512 == 0);
2921
2922 Assert(pImage->cbSector);
2923 Assert(pvBuf);
2924
2925 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2926 {
2927 rc = VERR_VD_IMAGE_READ_ONLY;
2928 goto out;
2929 }
2930
2931 *pcbPreRead = 0;
2932 *pcbPostRead = 0;
2933
2934 /*
2935 * Clip write size to a value which is supported by the target.
2936 */
2937 cbToWrite = RT_MIN(cbToWrite, pImage->cbSendDataLength);
2938
2939 lba = uOffset / pImage->cbSector;
2940 tls = (uint16_t)(cbToWrite / pImage->cbSector);
2941 SCSIREQ sr;
2942 uint8_t cdb[10];
2943 uint8_t sense[32];
2944
2945 cdb[0] = SCSI_WRITE_10;
2946 cdb[1] = 0; /* reserved */
2947 cdb[2] = (lba >> 24) & 0xff;
2948 cdb[3] = (lba >> 16) & 0xff;
2949 cdb[4] = (lba >> 8) & 0xff;
2950 cdb[5] = lba & 0xff;
2951 cdb[6] = 0; /* reserved */
2952 cdb[7] = (tls >> 8) & 0xff;
2953 cdb[8] = tls & 0xff;
2954 cdb[9] = 0; /* control */
2955
2956 sr.enmXfer = SCSIXFER_TO_TARGET;
2957 sr.cbCmd = sizeof(cdb);
2958 sr.pvCmd = cdb;
2959 sr.cbI2TData = cbToWrite;
2960 sr.pcvI2TData = pvBuf;
2961 sr.cbT2IData = 0;
2962 sr.pvT2IData = NULL;
2963 sr.cbSense = sizeof(sense);
2964 sr.pvSense = sense;
2965
2966 for (unsigned i = 0; i < 10; i++)
2967 {
2968 rc = iscsiCommand(pImage, &sr);
2969 if ( (RT_SUCCESS(rc) && !sr.cbSense)
2970 || RT_FAILURE(rc))
2971 break;
2972 rc = VERR_WRITE_ERROR;
2973 }
2974 if (RT_FAILURE(rc))
2975 {
2976 AssertMsgFailed(("iscsiCommand(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
2977 *pcbWriteProcess = 0;
2978 }
2979 else
2980 *pcbWriteProcess = cbToWrite;
2981
2982out:
2983 LogFlowFunc(("returns %Rrc\n", rc));
2984 return rc;
2985}
2986
2987
2988/** @copydoc VBOXHDDBACKEND::pfnFlush */
2989static int iscsiFlush(void *pBackendData)
2990{
2991 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2992 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
2993 int rc;
2994
2995 Assert(pImage);
2996
2997 SCSIREQ sr;
2998 uint8_t cdb[10];
2999 uint8_t sense[32];
3000
3001 cdb[0] = SCSI_SYNCHRONIZE_CACHE;
3002 cdb[1] = 0; /* reserved */
3003 cdb[2] = 0; /* LBA 0 */
3004 cdb[3] = 0; /* LBA 0 */
3005 cdb[4] = 0; /* LBA 0 */
3006 cdb[5] = 0; /* LBA 0 */
3007 cdb[6] = 0; /* reserved */
3008 cdb[7] = 0; /* transfer everything to disk */
3009 cdb[8] = 0; /* transfer everything to disk */
3010 cdb[9] = 0; /* control */
3011
3012 sr.enmXfer = SCSIXFER_TO_TARGET;
3013 sr.cbCmd = sizeof(cdb);
3014 sr.pvCmd = cdb;
3015 sr.cbI2TData = 0;
3016 sr.pcvI2TData = NULL;
3017 sr.cbT2IData = 0;
3018 sr.pvT2IData = NULL;
3019 sr.cbSense = sizeof(sense);
3020 sr.pvSense = sense;
3021
3022 rc = iscsiCommand(pImage, &sr);
3023 if (RT_FAILURE(rc))
3024 AssertMsgFailed(("iscsiCommand(%s) -> %Rrc\n", pImage->pszTargetName, rc));
3025 LogFlowFunc(("returns %Rrc\n", rc));
3026 return rc;
3027}
3028
3029
3030/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
3031static unsigned iscsiGetVersion(void *pBackendData)
3032{
3033 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
3034 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3035
3036 Assert(pImage);
3037 NOREF(pImage);
3038
3039 return 0;
3040}
3041
3042
3043/** @copydoc VBOXHDDBACKEND::pfnGetSize */
3044static uint64_t iscsiGetSize(void *pBackendData)
3045{
3046 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
3047 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3048
3049 Assert(pImage);
3050
3051 if (pImage)
3052 return pImage->cbSize;
3053 else
3054 return 0;
3055}
3056
3057
3058/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
3059static uint64_t iscsiGetFileSize(void *pBackendData)
3060{
3061 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
3062 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3063
3064 Assert(pImage);
3065 NOREF(pImage);
3066
3067 if (pImage)
3068 return pImage->cbSize;
3069 else
3070 return 0;
3071}
3072
3073
3074/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
3075static int iscsiGetPCHSGeometry(void *pBackendData,
3076 PPDMMEDIAGEOMETRY pPCHSGeometry)
3077{
3078 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
3079 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3080 int rc;
3081
3082 Assert(pImage);
3083
3084 if (pImage)
3085 rc = VERR_VD_GEOMETRY_NOT_SET;
3086 else
3087 rc = VERR_VD_NOT_OPENED;
3088
3089 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
3090 return rc;
3091}
3092
3093
3094/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
3095static int iscsiSetPCHSGeometry(void *pBackendData,
3096 PCPDMMEDIAGEOMETRY pPCHSGeometry)
3097{
3098 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
3099 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3100 int rc;
3101
3102 Assert(pImage);
3103
3104 if (pImage)
3105 {
3106 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
3107 {
3108 rc = VERR_VD_IMAGE_READ_ONLY;
3109 goto out;
3110 }
3111 rc = VERR_VD_GEOMETRY_NOT_SET;
3112 }
3113 else
3114 rc = VERR_VD_NOT_OPENED;
3115
3116out:
3117 LogFlowFunc(("returns %Rrc\n", rc));
3118 return rc;
3119}
3120
3121
3122/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
3123static int iscsiGetLCHSGeometry(void *pBackendData,
3124 PPDMMEDIAGEOMETRY pLCHSGeometry)
3125{
3126 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
3127 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3128 int rc;
3129
3130 Assert(pImage);
3131
3132 if (pImage)
3133 rc = VERR_VD_GEOMETRY_NOT_SET;
3134 else
3135 rc = VERR_VD_NOT_OPENED;
3136
3137 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
3138 return rc;
3139}
3140
3141
3142/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
3143static unsigned iscsiGetImageFlags(void *pBackendData)
3144{
3145 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
3146 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3147 unsigned uImageFlags;
3148
3149 Assert(pImage);
3150 NOREF(pImage);
3151
3152 uImageFlags = VD_IMAGE_FLAGS_FIXED;
3153
3154 LogFlowFunc(("returns %#x\n", uImageFlags));
3155 return uImageFlags;
3156}
3157
3158
3159/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
3160static unsigned iscsiGetOpenFlags(void *pBackendData)
3161{
3162 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
3163 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3164 unsigned uOpenFlags;
3165
3166 Assert(pImage);
3167
3168 if (pImage)
3169 uOpenFlags = pImage->uOpenFlags;
3170 else
3171 uOpenFlags = 0;
3172
3173 LogFlowFunc(("returns %#x\n", uOpenFlags));
3174 return uOpenFlags;
3175}
3176
3177
3178/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
3179static int iscsiSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
3180{
3181 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
3182 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3183 int rc;
3184
3185 /* Image must be opened and the new flags must be valid. Just readonly and
3186 * info flags are supported. */
3187 if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO)))
3188 {
3189 rc = VERR_INVALID_PARAMETER;
3190 goto out;
3191 }
3192
3193 /* Implement this operation via reopening the image. */
3194 iscsiFreeImage(pImage, false);
3195 rc = iscsiOpenImage(pImage, uOpenFlags);
3196
3197out:
3198 LogFlowFunc(("returns %Rrc\n", rc));
3199 return rc;
3200}
3201
3202
3203/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
3204static int iscsiSetLCHSGeometry(void *pBackendData,
3205 PCPDMMEDIAGEOMETRY pLCHSGeometry)
3206{
3207 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
3208 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3209 int rc;
3210
3211 Assert(pImage);
3212
3213 if (pImage)
3214 {
3215 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
3216 {
3217 rc = VERR_VD_IMAGE_READ_ONLY;
3218 goto out;
3219 }
3220 rc = VERR_VD_GEOMETRY_NOT_SET;
3221 }
3222 else
3223 rc = VERR_VD_NOT_OPENED;
3224
3225out:
3226 LogFlowFunc(("returns %Rrc\n", rc));
3227 return rc;
3228}
3229
3230
3231/** @copydoc VBOXHDDBACKEND::pfnGetComment */
3232static int iscsiGetComment(void *pBackendData, char *pszComment,
3233 size_t cbComment)
3234{
3235 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
3236 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3237 int rc;
3238
3239 Assert(pImage);
3240
3241 if (pImage)
3242 rc = VERR_NOT_SUPPORTED;
3243 else
3244 rc = VERR_VD_NOT_OPENED;
3245
3246 LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
3247 return rc;
3248}
3249
3250
3251/** @copydoc VBOXHDDBACKEND::pfnSetComment */
3252static int iscsiSetComment(void *pBackendData, const char *pszComment)
3253{
3254 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
3255 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3256 int rc;
3257
3258 Assert(pImage);
3259
3260 if (pImage)
3261 {
3262 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3263 rc = VERR_NOT_SUPPORTED;
3264 else
3265 rc = VERR_VD_IMAGE_READ_ONLY;
3266 }
3267 else
3268 rc = VERR_VD_NOT_OPENED;
3269
3270 LogFlowFunc(("returns %Rrc\n", rc));
3271 return rc;
3272}
3273
3274
3275/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
3276static int iscsiGetUuid(void *pBackendData, PRTUUID pUuid)
3277{
3278 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
3279 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3280 int rc;
3281
3282 Assert(pImage);
3283
3284 if (pImage)
3285 rc = VERR_NOT_SUPPORTED;
3286 else
3287 rc = VERR_VD_NOT_OPENED;
3288
3289 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
3290 return rc;
3291}
3292
3293
3294/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
3295static int iscsiSetUuid(void *pBackendData, PCRTUUID pUuid)
3296{
3297 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
3298 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3299 int rc;
3300
3301 LogFlowFunc(("%RTuuid\n", pUuid));
3302 Assert(pImage);
3303
3304 if (pImage)
3305 {
3306 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3307 rc = VERR_NOT_SUPPORTED;
3308 else
3309 rc = VERR_VD_IMAGE_READ_ONLY;
3310 }
3311 else
3312 rc = VERR_VD_NOT_OPENED;
3313
3314 LogFlowFunc(("returns %Rrc\n", rc));
3315 return rc;
3316}
3317
3318
3319/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
3320static int iscsiGetModificationUuid(void *pBackendData, PRTUUID pUuid)
3321{
3322 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
3323 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3324 int rc;
3325
3326 Assert(pImage);
3327
3328 if (pImage)
3329 rc = VERR_NOT_SUPPORTED;
3330 else
3331 rc = VERR_VD_NOT_OPENED;
3332
3333 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
3334 return rc;
3335}
3336
3337
3338/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
3339static int iscsiSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
3340{
3341 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
3342 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3343 int rc;
3344
3345 LogFlowFunc(("%RTuuid\n", pUuid));
3346 Assert(pImage);
3347
3348 if (pImage)
3349 {
3350 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3351 rc = VERR_NOT_SUPPORTED;
3352 else
3353 rc = VERR_VD_IMAGE_READ_ONLY;
3354 }
3355 else
3356 rc = VERR_VD_NOT_OPENED;
3357
3358 LogFlowFunc(("returns %Rrc\n", rc));
3359 return rc;
3360}
3361
3362
3363/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
3364static int iscsiGetParentUuid(void *pBackendData, PRTUUID pUuid)
3365{
3366 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
3367 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3368 int rc;
3369
3370 Assert(pImage);
3371
3372 if (pImage)
3373 rc = VERR_NOT_SUPPORTED;
3374 else
3375 rc = VERR_VD_NOT_OPENED;
3376
3377 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
3378 return rc;
3379}
3380
3381
3382/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
3383static int iscsiSetParentUuid(void *pBackendData, PCRTUUID pUuid)
3384{
3385 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
3386 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3387 int rc;
3388
3389 LogFlowFunc(("%RTuuid\n", pUuid));
3390 Assert(pImage);
3391
3392 if (pImage)
3393 {
3394 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3395 rc = VERR_NOT_SUPPORTED;
3396 else
3397 rc = VERR_VD_IMAGE_READ_ONLY;
3398 }
3399 else
3400 rc = VERR_VD_NOT_OPENED;
3401
3402 LogFlowFunc(("returns %Rrc\n", rc));
3403 return rc;
3404}
3405
3406
3407/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
3408static int iscsiGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
3409{
3410 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
3411 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3412 int rc;
3413
3414 Assert(pImage);
3415
3416 if (pImage)
3417 rc = VERR_NOT_SUPPORTED;
3418 else
3419 rc = VERR_VD_NOT_OPENED;
3420
3421 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
3422 return rc;
3423}
3424
3425
3426/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
3427static int iscsiSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
3428{
3429 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
3430 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3431 int rc;
3432
3433 LogFlowFunc(("%RTuuid\n", pUuid));
3434 Assert(pImage);
3435
3436 if (pImage)
3437 {
3438 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3439 rc = VERR_NOT_SUPPORTED;
3440 else
3441 rc = VERR_VD_IMAGE_READ_ONLY;
3442 }
3443 else
3444 rc = VERR_VD_NOT_OPENED;
3445
3446 LogFlowFunc(("returns %Rrc\n", rc));
3447 return rc;
3448}
3449
3450
3451/** @copydoc VBOXHDDBACKEND::pfnDump */
3452static void iscsiDump(void *pBackendData)
3453{
3454 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3455
3456 Assert(pImage);
3457 if (pImage)
3458 {
3459 /** @todo put something useful here */
3460 pImage->pInterfaceErrorCallbacks->pfnMessage(pImage->pInterfaceError->pvUser, "Header: cVolume=%u\n", pImage->cVolume);
3461 }
3462}
3463
3464
3465/** @copydoc VBOXHDDBACKEND::pfnGetTimeStamp */
3466static int iscsiGetTimeStamp(void *pBackendData, PRTTIMESPEC pTimeStamp)
3467{
3468 LogFlowFunc(("pBackendData=%#p pTimeStamp=%#p\n", pBackendData, pTimeStamp));
3469 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3470 int rc = VERR_NOT_SUPPORTED;
3471
3472 Assert(pImage);
3473 NOREF(pImage);
3474
3475 LogFlowFunc(("returns %Rrc\n", rc));
3476 return rc;
3477}
3478
3479
3480/** @copydoc VBOXHDDBACKEND::pfnGetParentTimeStamp */
3481static int iscsiGetParentTimeStamp(void *pBackendData, PRTTIMESPEC pTimeStamp)
3482{
3483 LogFlowFunc(("pBackendData=%#p pTimeStamp=%#p\n", pBackendData, pTimeStamp));
3484 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3485 int rc = VERR_NOT_SUPPORTED;
3486
3487 Assert(pImage);
3488 NOREF(pImage);
3489
3490 LogFlowFunc(("returns %Rrc\n", rc));
3491 return rc;
3492}
3493
3494
3495/** @copydoc VBOXHDDBACKEND::pfnSetParentTimeStamp */
3496static int iscsiSetParentTimeStamp(void *pBackendData, PCRTTIMESPEC pTimeStamp)
3497{
3498 LogFlowFunc(("pBackendData=%#p pTimeStamp=%#p\n", pBackendData, pTimeStamp));
3499 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3500 int rc = VERR_NOT_SUPPORTED;
3501
3502 Assert(pImage);
3503 NOREF(pImage);
3504
3505 LogFlowFunc(("returns %Rrc\n", rc));
3506 return rc;
3507}
3508
3509
3510/** @copydoc VBOXHDDBACKEND::pfnGetParentFilename */
3511static int iscsiGetParentFilename(void *pBackendData, char **ppszParentFilename)
3512{
3513 LogFlowFunc(("pBackendData=%#p ppszParentFilename=%#p\n", pBackendData, ppszParentFilename));
3514 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3515 int rc = VERR_NOT_SUPPORTED;
3516
3517 Assert(pImage);
3518 NOREF(pImage);
3519
3520 LogFlowFunc(("returns %Rrc\n", rc));
3521 return rc;
3522}
3523
3524
3525/** @copydoc VBOXHDDBACKEND::pfnSetParentFilename */
3526static int iscsiSetParentFilename(void *pBackendData, const char *pszParentFilename)
3527{
3528 LogFlowFunc(("pBackendData=%#p pszParentFilename=%s\n", pBackendData, pszParentFilename));
3529 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
3530 int rc = VERR_NOT_SUPPORTED;
3531
3532 Assert(pImage);
3533 NOREF(pImage);
3534
3535 LogFlowFunc(("returns %Rrc\n", rc));
3536 return rc;
3537}
3538
3539/** @copydoc VBOXHDDBACKEND::pfnComposeLocation */
3540static int iscsiComposeLocation(PVDINTERFACE pConfig, char **pszLocation)
3541{
3542 char *pszTarget = NULL;
3543 char *pszLUN = NULL;
3544 char *pszAddress = NULL;
3545 int rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetName", &pszTarget);
3546 if (RT_SUCCESS(rc))
3547 {
3548 rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "LUN", &pszLUN);
3549 if (RT_SUCCESS(rc))
3550 {
3551 rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetAddress", &pszAddress);
3552 if (RT_SUCCESS(rc))
3553 {
3554 if (RTStrAPrintf(pszLocation, "iscsi://%s/%s/%s",
3555 pszAddress, pszTarget, pszLUN) < 0)
3556 rc = VERR_NO_MEMORY;
3557 }
3558 }
3559 }
3560 RTMemFree(pszTarget);
3561 RTMemFree(pszLUN);
3562 RTMemFree(pszAddress);
3563 return rc;
3564}
3565
3566/** @copydoc VBOXHDDBACKEND::pfnComposeName */
3567static int iscsiComposeName(PVDINTERFACE pConfig, char **pszName)
3568{
3569 char *pszTarget = NULL;
3570 char *pszLUN = NULL;
3571 char *pszAddress = NULL;
3572 int rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetName", &pszTarget);
3573 if (RT_SUCCESS(rc))
3574 {
3575 rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "LUN", &pszLUN);
3576 if (RT_SUCCESS(rc))
3577 {
3578 rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetAddress", &pszAddress);
3579 if (RT_SUCCESS(rc))
3580 {
3581 /** @todo think about a nicer looking location scheme for iSCSI */
3582 if (RTStrAPrintf(pszName, "%s/%s/%s",
3583 pszAddress, pszTarget, pszLUN) < 0)
3584 rc = VERR_NO_MEMORY;
3585 }
3586 }
3587 }
3588 RTMemFree(pszTarget);
3589 RTMemFree(pszLUN);
3590 RTMemFree(pszAddress);
3591
3592 return rc;
3593}
3594
3595
3596VBOXHDDBACKEND g_ISCSIBackend =
3597{
3598 /* pszBackendName */
3599 "iSCSI",
3600 /* cbSize */
3601 sizeof(VBOXHDDBACKEND),
3602 /* uBackendCaps */
3603 VD_CAP_CONFIG | VD_CAP_TCPNET,
3604 /* papszFileExtensions */
3605 NULL,
3606 /* paConfigInfo */
3607 s_iscsiConfigInfo,
3608 /* hPlugin */
3609 NIL_RTLDRMOD,
3610 /* pfnCheckIfValid */
3611 iscsiCheckIfValid,
3612 /* pfnOpen */
3613 iscsiOpen,
3614 /* pfnCreate */
3615 iscsiCreate,
3616 /* pfnRename */
3617 iscsiRename,
3618 /* pfnClose */
3619 iscsiClose,
3620 /* pfnRead */
3621 iscsiRead,
3622 /* pfnWrite */
3623 iscsiWrite,
3624 /* pfnFlush */
3625 iscsiFlush,
3626 /* pfnGetVersion */
3627 iscsiGetVersion,
3628 /* pfnGetSize */
3629 iscsiGetSize,
3630 /* pfnGetFileSize */
3631 iscsiGetFileSize,
3632 /* pfnGetPCHSGeometry */
3633 iscsiGetPCHSGeometry,
3634 /* pfnSetPCHSGeometry */
3635 iscsiSetPCHSGeometry,
3636 /* pfnGetLCHSGeometry */
3637 iscsiGetLCHSGeometry,
3638 /* pfnSetLCHSGeometry */
3639 iscsiSetLCHSGeometry,
3640 /* pfnGetImageFlags */
3641 iscsiGetImageFlags,
3642 /* pfnGetOpenFlags */
3643 iscsiGetOpenFlags,
3644 /* pfnSetOpenFlags */
3645 iscsiSetOpenFlags,
3646 /* pfnGetComment */
3647 iscsiGetComment,
3648 /* pfnSetComment */
3649 iscsiSetComment,
3650 /* pfnGetUuid */
3651 iscsiGetUuid,
3652 /* pfnSetUuid */
3653 iscsiSetUuid,
3654 /* pfnGetModificationUuid */
3655 iscsiGetModificationUuid,
3656 /* pfnSetModificationUuid */
3657 iscsiSetModificationUuid,
3658 /* pfnGetParentUuid */
3659 iscsiGetParentUuid,
3660 /* pfnSetParentUuid */
3661 iscsiSetParentUuid,
3662 /* pfnGetParentModificationUuid */
3663 iscsiGetParentModificationUuid,
3664 /* pfnSetParentModificationUuid */
3665 iscsiSetParentModificationUuid,
3666 /* pfnDump */
3667 iscsiDump,
3668 /* pfnGetTimeStamp */
3669 iscsiGetTimeStamp,
3670 /* pfnGetParentTimeStamp */
3671 iscsiGetParentTimeStamp,
3672 /* pfnSetParentTimeStamp */
3673 iscsiSetParentTimeStamp,
3674 /* pfnGetParentFilename */
3675 iscsiGetParentFilename,
3676 /* pfnSetParentFilename */
3677 iscsiSetParentFilename,
3678 /* pfnIsAsyncIOSupported */
3679 NULL,
3680 /* pfnAsyncRead */
3681 NULL,
3682 /* pfnAsyncWrite */
3683 NULL,
3684 /* pfnComposeLocation */
3685 iscsiComposeLocation,
3686 /* pfnComposeName */
3687 iscsiComposeName
3688};
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette