VirtualBox

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

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

VBoxHDD: Make the non blocking read/write socket APIs available in the VDTCPNET interface

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

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