VirtualBox

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

Last change on this file since 31193 was 31098, checked in by vboxsync, 14 years ago

iSCSI: First part for async I/O. Move I/O into a separate thread and handle NOP-in requests properly to prevent disconnects if the guest isn't doing any I/O.

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

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