VirtualBox

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

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

Storage/iSCSI: fix parameter negotiation inconsistency

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