VirtualBox

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

Last change on this file since 33000 was 32691, checked in by vboxsync, 14 years ago

Storage/VBoxHDD+iSCSI+DrvVD,Main/Medium: Make the IO and TCPNET interfaces per i
mage, fix comments, remove forgotten dead code, handle disk and image interfaces

separately in Medium, whitespace cleanup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 191.8 KB
Line 
1/* $Id: ISCSIHDDCore.cpp 32691 2010-09-22 10:09:54Z 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/string.h>
33#include <iprt/asm.h>
34#include <iprt/thread.h>
35#include <iprt/semaphore.h>
36#include <iprt/md5.h>
37#include <iprt/tcp.h>
38#include <iprt/time.h>
39#include <VBox/scsi.h>
40
41
42/*******************************************************************************
43* Defined Constants And Macros *
44*******************************************************************************/
45
46/** Default port number to use for iSCSI. */
47#define ISCSI_DEFAULT_PORT 3260
48
49
50/** Converts a number in the range of 0 - 15 into the corresponding hex char. */
51#define NUM_2_HEX(b) ('0' + (b) + (((b) > 9) ? 39 : 0))
52/** Converts a hex char into the corresponding number in the range 0-15. */
53#define HEX_2_NUM(c) (((c) <= '9') ? ((c) - '0') : (((c - 'A' + 10) & 0xf)))
54/* Converts a base64 char into the corresponding number in the range 0-63. */
55#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)
56
57
58/** Minumum CHAP_MD5 challenge length in bytes. */
59#define CHAP_MD5_CHALLENGE_MIN 16
60/** Maximum CHAP_MD5 challenge length in bytes. */
61#define CHAP_MD5_CHALLENGE_MAX 24
62
63
64/**
65 * SCSI peripheral device type. */
66typedef enum SCSIDEVTYPE
67{
68 /** direct-access device. */
69 SCSI_DEVTYPE_DISK = 0,
70 /** sequential-access device. */
71 SCSI_DEVTYPE_TAPE,
72 /** printer device. */
73 SCSI_DEVTYPE_PRINTER,
74 /** processor device. */
75 SCSI_DEVTYPE_PROCESSOR,
76 /** write-once device. */
77 SCSI_DEVTYPE_WORM,
78 /** CD/DVD device. */
79 SCSI_DEVTYPE_CDROM,
80 /** scanner device. */
81 SCSI_DEVTYPE_SCANNER,
82 /** optical memory device. */
83 SCSI_DEVTYPE_OPTICAL,
84 /** medium changer. */
85 SCSI_DEVTYPE_CHANGER,
86 /** communications device. */
87 SCSI_DEVTYPE_COMMUNICATION,
88 /** storage array controller device. */
89 SCSI_DEVTYPE_RAIDCTL = 0x0c,
90 /** enclosure services device. */
91 SCSI_DEVTYPE_ENCLOSURE,
92 /** simplified direct-access device. */
93 SCSI_DEVTYPE_SIMPLEDISK,
94 /** optical card reader/writer device. */
95 SCSI_DEVTYPE_OCRW,
96 /** bridge controller device. */
97 SCSI_DEVTYPE_BRIDGE,
98 /** object-based storage device. */
99 SCSI_DEVTYPE_OSD
100} SCSIDEVTYPE;
101
102/** Mask for extracting the SCSI device type out of the first byte of the INQUIRY response. */
103#define SCSI_DEVTYPE_MASK 0x1f
104
105/** Mask to extract the CmdQue bit out of the seventh byte of the INQUIRY resposne. */
106#define SCSI_INQUIRY_CMDQUE_MASK 0x02
107
108/** Maximum PDU payload size we can handle in one piece. Greater or equal than
109 * s_iscsiConfigDefaultWriteSplit. */
110#define ISCSI_DATA_LENGTH_MAX _256K
111
112/** Maximum PDU size we can handle in one piece. */
113#define ISCSI_RECV_PDU_BUFFER_SIZE (ISCSI_DATA_LENGTH_MAX + ISCSI_BHS_SIZE)
114
115
116/** Version of the iSCSI standard which this initiator driver can handle. */
117#define ISCSI_MY_VERSION 0
118
119
120/** Length of ISCSI basic header segment. */
121#define ISCSI_BHS_SIZE 48
122
123
124/** Reserved task tag value. */
125#define ISCSI_TASK_TAG_RSVD 0xffffffff
126
127
128/**
129 * iSCSI opcodes. */
130typedef enum ISCSIOPCODE
131{
132 /** NOP-Out. */
133 ISCSIOP_NOP_OUT = 0x00000000,
134 /** SCSI command. */
135 ISCSIOP_SCSI_CMD = 0x01000000,
136 /** SCSI task management request. */
137 ISCSIOP_SCSI_TASKMGMT_REQ = 0x02000000,
138 /** Login request. */
139 ISCSIOP_LOGIN_REQ = 0x03000000,
140 /** Text request. */
141 ISCSIOP_TEXT_REQ = 0x04000000,
142 /** SCSI Data-Out. */
143 ISCSIOP_SCSI_DATA_OUT = 0x05000000,
144 /** Logout request. */
145 ISCSIOP_LOGOUT_REQ = 0x06000000,
146 /** SNACK request. */
147 ISCSIOP_SNACK_REQ = 0x10000000,
148
149 /** NOP-In. */
150 ISCSIOP_NOP_IN = 0x20000000,
151 /** SCSI response. */
152 ISCSIOP_SCSI_RES = 0x21000000,
153 /** SCSI Task Management response. */
154 ISCSIOP_SCSI_TASKMGMT_RES = 0x22000000,
155 /** Login response. */
156 ISCSIOP_LOGIN_RES = 0x23000000,
157 /** Text response. */
158 ISCSIOP_TEXT_RES = 0x24000000,
159 /** SCSI Data-In. */
160 ISCSIOP_SCSI_DATA_IN = 0x25000000,
161 /** Logout response. */
162 ISCSIOP_LOGOUT_RES = 0x26000000,
163 /** Ready To Transfer (R2T). */
164 ISCSIOP_R2T = 0x31000000,
165 /** Asynchronous message. */
166 ISCSIOP_ASYN_MSG = 0x32000000,
167 /** Reject. */
168 ISCSIOP_REJECT = 0x3f000000
169} ISCSIOPCODE;
170
171/** Mask for extracting the iSCSI opcode out of the first header word. */
172#define ISCSIOP_MASK 0x3f000000
173
174
175/** ISCSI BHS word 0: Request should be processed immediately. */
176#define ISCSI_IMMEDIATE_DELIVERY_BIT 0x40000000
177
178/** ISCSI BHS word 0: This is the final PDU for this request/response. */
179#define ISCSI_FINAL_BIT 0x00800000
180/** ISCSI BHS word 0: Mask for extracting the CSG. */
181#define ISCSI_CSG_MASK 0x000c0000
182/** ISCSI BHS word 0: Shift offset for extracting the CSG. */
183#define ISCSI_CSG_SHIFT 18
184/** ISCSI BHS word 0: Mask for extracting the NSG. */
185#define ISCSI_NSG_MASK 0x00030000
186/** ISCSI BHS word 0: Shift offset for extracting the NSG. */
187#define ISCSI_NSG_SHIFT 16
188
189/** ISCSI BHS word 0: task attribute untagged */
190#define ISCSI_TASK_ATTR_UNTAGGED 0x00000000
191/** ISCSI BHS word 0: task attribute simple */
192#define ISCSI_TASK_ATTR_SIMPLE 0x00010000
193/** ISCSI BHS word 0: task attribute ordered */
194#define ISCSI_TASK_ATTR_ORDERED 0x00020000
195/** ISCSI BHS word 0: task attribute head of queue */
196#define ISCSI_TASK_ATTR_HOQ 0x00030000
197/** ISCSI BHS word 0: task attribute ACA */
198#define ISCSI_TASK_ATTR_ACA 0x00040000
199
200/** ISCSI BHS word 0: transit to next login phase. */
201#define ISCSI_TRANSIT_BIT 0x00800000
202/** ISCSI BHS word 0: continue with login negotiation. */
203#define ISCSI_CONTINUE_BIT 0x00400000
204
205/** ISCSI BHS word 0: residual underflow. */
206#define ISCSI_RESIDUAL_UNFL_BIT 0x00020000
207/** ISCSI BHS word 0: residual overflow. */
208#define ISCSI_RESIDUAL_OVFL_BIT 0x00040000
209/** ISCSI BHS word 0: Bidirectional read residual underflow. */
210#define ISCSI_BI_READ_RESIDUAL_UNFL_BIT 0x00080000
211/** ISCSI BHS word 0: Bidirectional read residual overflow. */
212#define ISCSI_BI_READ_RESIDUAL_OVFL_BIT 0x00100000
213
214/** ISCSI BHS word 0: SCSI response mask. */
215#define ISCSI_SCSI_RESPONSE_MASK 0x0000ff00
216/** ISCSI BHS word 0: SCSI status mask. */
217#define ISCSI_SCSI_STATUS_MASK 0x000000ff
218
219/** ISCSI BHS word 0: response includes status. */
220#define ISCSI_STATUS_BIT 0x00010000
221
222/** Maximum number of scatter/gather segments needed to send a PDU. */
223#define ISCSI_SG_SEGMENTS_MAX 4
224
225/** Number of entries in the command table. */
226#define ISCSI_CMD_WAITING_ENTRIES 32
227
228/**
229 * iSCSI login status class. */
230typedef enum ISCSILOGINSTATUSCLASS
231{
232 /** Success. */
233 ISCSI_LOGIN_STATUS_CLASS_SUCCESS = 0,
234 /** Redirection. */
235 ISCSI_LOGIN_STATUS_CLASS_REDIRECTION,
236 /** Initiator error. */
237 ISCSI_LOGIN_STATUS_CLASS_INITIATOR_ERROR,
238 /** Target error. */
239 ISCSI_LOGIN_STATUS_CLASS_TARGET_ERROR
240} ISCSILOGINSTATUSCLASS;
241
242
243/**
244 * iSCSI connection state. */
245typedef enum ISCSISTATE
246{
247 /** Not having a connection/session at all. */
248 ISCSISTATE_FREE,
249 /** Currently trying to login. */
250 ISCSISTATE_IN_LOGIN,
251 /** Normal operation, corresponds roughly to the Full Feature Phase. */
252 ISCSISTATE_NORMAL,
253 /** Currently trying to logout. */
254 ISCSISTATE_IN_LOGOUT
255} ISCSISTATE;
256
257/**
258 * iSCSI PDU send flags (and maybe more in the future). */
259typedef enum ISCSIPDUFLAGS
260{
261 /** No special flags */
262 ISCSIPDU_DEFAULT = 0,
263 /** Do not attempt to re-attach to the target if the connection is lost */
264 ISCSIPDU_NO_REATTACH = RT_BIT(1)
265} ISCSIPDUFLAGS;
266
267
268/*******************************************************************************
269* Structures and Typedefs *
270*******************************************************************************/
271
272/**
273 * iSCSI login negotiation parameter
274 */
275typedef struct ISCSIPARAMETER
276{
277 /** Name of the parameter. */
278 const char *pszParamName;
279 /** Value of the parameter. */
280 const char *pszParamValue;
281 /** Length of the binary parameter. 0=zero-terminated string. */
282 size_t cbParamValue;
283} ISCSIPARAMETER;
284
285
286/**
287 * iSCSI Response PDU buffer (scatter).
288 */
289typedef struct ISCSIRES
290{
291 /** Length of PDU segment. */
292 size_t cbSeg;
293 /** Pointer to PDU segment. */
294 void *pvSeg;
295} ISCSIRES;
296/** Pointer to an iSCSI Response PDU buffer. */
297typedef ISCSIRES *PISCSIRES;
298/** Pointer to a const iSCSI Response PDU buffer. */
299typedef ISCSIRES const *PCISCSIRES;
300
301
302/**
303 * iSCSI Request PDU buffer (gather).
304 */
305typedef struct ISCSIREQ
306{
307 /** Length of PDU segment in bytes. */
308 size_t cbSeg;
309 /** Pointer to PDU segment. */
310 const void *pcvSeg;
311} ISCSIREQ;
312/** Pointer to an iSCSI Request PDU buffer. */
313typedef ISCSIREQ *PISCSIREQ;
314/** Pointer to a const iSCSI Request PDU buffer. */
315typedef ISCSIREQ const *PCISCSIREQ;
316
317
318/**
319 * SCSI transfer directions.
320 */
321typedef enum SCSIXFER
322{
323 SCSIXFER_NONE = 0,
324 SCSIXFER_TO_TARGET,
325 SCSIXFER_FROM_TARGET,
326 SCSIXFER_TO_FROM_TARGET
327} SCSIXFER, *PSCSIXFER;
328
329/** Forward declaration. */
330typedef struct ISCSIIMAGE *PISCSIIMAGE;
331
332/**
333 * SCSI request structure.
334 */
335typedef struct SCSIREQ
336{
337 /** Transfer direction. */
338 SCSIXFER enmXfer;
339 /** Length of command block. */
340 size_t cbCDB;
341 /** Length of Initiator2Target data buffer. */
342 size_t cbI2TData;
343 /** Length of Target2Initiator data buffer. */
344 size_t cbT2IData;
345 /** Length of sense buffer
346 * This contains the number of sense bytes received upon completion. */
347 size_t cbSense;
348 /** Completion status of the command. */
349 uint8_t status;
350 /** Pointer to command block. */
351 void *pvCDB;
352 /** Pointer to sense buffer. */
353 void *pvSense;
354 /** Pointer to the Initiator2Target S/G list. */
355 PRTSGSEG paI2TSegs;
356 /** Number of entries in the I2T S/G list. */
357 unsigned cI2TSegs;
358 /** Pointer to the Target2Initiator S/G list. */
359 PRTSGSEG paT2ISegs;
360 /** Number of entries in the T2I S/G list. */
361 unsigned cT2ISegs;
362 /** S/G buffer for the target to initiator bits. */
363 RTSGBUF SgBufT2I;
364} SCSIREQ, *PSCSIREQ;
365
366/**
367 * Async request structure holding all necessary data for
368 * request processing.
369 */
370typedef struct SCSIREQASYNC
371{
372 /** I/O context associated with this request. */
373 PVDIOCTX pIoCtx;
374 /** Pointer to the SCSI request structure. */
375 PSCSIREQ pScsiReq;
376 /** The CDB. */
377 uint8_t abCDB[16];
378 /** The sense buffer. */
379 uint8_t abSense[96];
380 /** Status code to return if we got sense data. */
381 int rcSense;
382 /** Number of retries if the command completes with sense
383 * data before we return with an error.
384 */
385 unsigned cSenseRetries;
386 /** The number of entries in the I2T S/G list. */
387 unsigned cI2TSegs;
388 /** The number of entries in the T2I S/G list. */
389 unsigned cT2ISegs;
390 /** The S/G list - variable in size.
391 * This array holds both the I2T and T2I segments.
392 * The I2T segments are first and the T2I are second.
393 */
394 RTSGSEG aSegs[1];
395} SCSIREQASYNC, *PSCSIREQASYNC;
396
397typedef enum ISCSICMDTYPE
398{
399 /** Process a SCSI request. */
400 ISCSICMDTYPE_REQ = 0,
401 /** Call a function in the I/O thread. */
402 ISCSICMDTYPE_EXEC,
403 /** Usual 32bit hack. */
404 ISCSICMDTYPE_32BIT_HACK = 0x7fffffff
405} ISCSICMDTYPE;
406
407
408/** The command completion function. */
409typedef DECLCALLBACK(void) FNISCSICMDCOMPLETED(PISCSIIMAGE pImage, int rcReq, void *pvUser);
410/** Pointer to a command completion function. */
411typedef FNISCSICMDCOMPLETED *PFNISCSICMDCOMPLETED;
412
413/** The command execution function. */
414typedef DECLCALLBACK(int) FNISCSIEXEC(void *pvUser);
415/** Pointer to a command execution function. */
416typedef FNISCSIEXEC *PFNISCSIEXEC;
417
418/**
419 * Structure used to complete a synchronous request.
420 */
421typedef struct ISCSICMDSYNC
422{
423 /** Event sempahore to wakeup the waiting thread. */
424 RTSEMEVENT EventSem;
425 /** Status code of the command. */
426 int rcCmd;
427} ISCSICMDSYNC, *PISCSICMDSYNC;
428
429/**
430 * iSCSI command.
431 * Used to forward requests to the I/O thread
432 * if existing.
433 */
434typedef struct ISCSICMD
435{
436 /** Next one in the list. */
437 struct ISCSICMD *pNext;
438 /** Assigned ITT. */
439 uint32_t Itt;
440 /** Completion callback. */
441 PFNISCSICMDCOMPLETED pfnComplete;
442 /** Opaque user data. */
443 void *pvUser;
444 /** Command to execute. */
445 ISCSICMDTYPE enmCmdType;
446 /** Command type dependent data. */
447 union
448 {
449 /** Process a SCSI request. */
450 struct
451 {
452 /** The SCSI request to process. */
453 PSCSIREQ pScsiReq;
454 } ScsiReq;
455 /** Call a function in the I/O thread. */
456 struct
457 {
458 /** The method to execute. */
459 PFNISCSIEXEC pfnExec;
460 /** User data. */
461 void *pvUser;
462 } Exec;
463 } CmdType;
464} ISCSICMD, *PISCSICMD;
465
466/**
467 * Send iSCSI PDU.
468 * Contains all necessary data to send a PDU.
469 */
470typedef struct ISCSIPDUTX
471{
472 /** Pointer to the next PDu to send. */
473 struct ISCSIPDUTX *pNext;
474 /** The BHS. */
475 uint32_t aBHS[12];
476 /** Assigned CmdSN for this PDU. */
477 uint32_t CmdSN;
478 /** The S/G buffer used for sending. */
479 RTSGBUF SgBuf;
480 /** Number of bytes to send until the PDU completed. */
481 size_t cbSgLeft;
482 /** The iSCSI command this PDU belongs to. */
483 PISCSICMD pIScsiCmd;
484 /** Number of segments in the request segments array. */
485 unsigned cISCSIReq;
486 /** The request segments - variable in size. */
487 RTSGSEG aISCSIReq[1];
488} ISCSIPDUTX, *PISCSIPDUTX;
489
490/**
491 * Block driver instance data.
492 */
493typedef struct ISCSIIMAGE
494{
495 /** Pointer to the filename (location). Not really used. */
496 const char *pszFilename;
497 /** Pointer to the initiator name. */
498 char *pszInitiatorName;
499 /** Pointer to the target name. */
500 char *pszTargetName;
501 /** Pointer to the target address. */
502 char *pszTargetAddress;
503 /** Pointer to the user name for authenticating the Initiator. */
504 char *pszInitiatorUsername;
505 /** Pointer to the secret for authenticating the Initiator. */
506 uint8_t *pbInitiatorSecret;
507 /** Length of the secret for authenticating the Initiator. */
508 size_t cbInitiatorSecret;
509 /** Pointer to the user name for authenticating the Target. */
510 char *pszTargetUsername;
511 /** Pointer to the secret for authenticating the Initiator. */
512 uint8_t *pbTargetSecret;
513 /** Length of the secret for authenticating the Initiator. */
514 size_t cbTargetSecret;
515 /** Limit for iSCSI writes, essentially limiting the amount of data
516 * written in a single write. This is negotiated with the target, so
517 * the actual size might be smaller. */
518 uint32_t cbWriteSplit;
519 /** Initiator session identifier. */
520 uint64_t ISID;
521 /** SCSI Logical Unit Number. */
522 uint64_t LUN;
523 /** Pointer to the per-disk VD interface list. */
524 PVDINTERFACE pVDIfsDisk;
525 /** Error interface. */
526 PVDINTERFACE pInterfaceError;
527 /** Error interface callback table. */
528 PVDINTERFACEERROR pInterfaceErrorCallbacks;
529 /** Pointer to the per-image VD interface list. */
530 PVDINTERFACE pVDIfsImage;
531 /** Config interface. */
532 PVDINTERFACE pInterfaceConfig;
533 /** Config interface callback table. */
534 PVDINTERFACECONFIG pInterfaceConfigCallbacks;
535 /** I/O interface. */
536 PVDINTERFACE pInterfaceIo;
537 /** I/O interface callback table. */
538 PVDINTERFACEIOINT pInterfaceIoCallbacks;
539 /** TCP network stack interface. */
540 PVDINTERFACE pInterfaceNet;
541 /** TCP network stack interface callback table. */
542 PVDINTERFACETCPNET pInterfaceNetCallbacks;
543 /** Image open flags. */
544 unsigned uOpenFlags;
545 /** Number of re-login retries when a connection fails. */
546 uint32_t cISCSIRetries;
547 /** Sector size on volume. */
548 uint32_t cbSector;
549 /** Size of volume in sectors. */
550 uint64_t cVolume;
551 /** Total volume size in bytes. Easier than multiplying the above values all the time. */
552 uint64_t cbSize;
553
554 /** Negotiated maximum data length when sending to target. */
555 uint32_t cbSendDataLength;
556 /** Negotiated maximum data length when receiving from target. */
557 uint32_t cbRecvDataLength;
558
559 /** Current state of the connection/session. */
560 ISCSISTATE state;
561 /** Flag whether the first Login Response PDU has been seen. */
562 bool FirstRecvPDU;
563 /** Initiator Task Tag of the last iSCSI request PDU. */
564 uint32_t ITT;
565 /** Sequence number of the last command. */
566 uint32_t CmdSN;
567 /** Sequence number of the next command expected by the target. */
568 uint32_t ExpCmdSN;
569 /** Maximum sequence number accepted by the target (determines size of window). */
570 uint32_t MaxCmdSN;
571 /** Expected sequence number of next status. */
572 uint32_t ExpStatSN;
573 /** Currently active request. */
574 PISCSIREQ paCurrReq;
575 /** Segment number of currently active request. */
576 uint32_t cnCurrReq;
577 /** Pointer to receive PDU buffer. (Freed by RT) */
578 void *pvRecvPDUBuf;
579 /** Length of receive PDU buffer. */
580 size_t cbRecvPDUBuf;
581 /** Mutex protecting against concurrent use from several threads. */
582 RTSEMMUTEX Mutex;
583
584 /** Pointer to the target hostname. */
585 char *pszHostname;
586 /** Pointer to the target hostname. */
587 uint32_t uPort;
588 /** Socket handle of the TCP connection. */
589 VDSOCKET Socket;
590 /** Timeout for read operations on the TCP connection (in milliseconds). */
591 uint32_t uReadTimeout;
592 /** Flag whether to automatically generate the initiator name. */
593 bool fAutomaticInitiatorName;
594 /** Flag whether to use the host IP stack or DevINIP. */
595 bool fHostIP;
596
597 /** Head of request queue */
598 PISCSICMD pScsiReqQueue;
599 /** Mutex protecting the request queue from concurrent access. */
600 RTSEMMUTEX MutexReqQueue;
601 /** I/O thread. */
602 RTTHREAD hThreadIo;
603 /** Flag whether the thread should be still running. */
604 volatile bool fRunning;
605 /* Flag whether the target supports command queuing. */
606 bool fCmdQueuingSupported;
607 /** Flag whether extended select is supported. */
608 bool fExtendedSelectSupported;
609 /** Padding used for aligning the PDUs. */
610 uint8_t aPadding[4];
611 /** Socket events to poll for. */
612 uint32_t fPollEvents;
613 /** Number of bytes to read to complete the current PDU. */
614 size_t cbRecvPDUResidual;
615 /** Current position in the PDU buffer. */
616 uint8_t *pbRecvPDUBufCur;
617 /** Flag whether we are currently reading the BHS. */
618 bool fRecvPDUBHS;
619 /** List of PDUs waiting to get transmitted. */
620 PISCSIPDUTX pIScsiPDUTxHead;
621 /** Tail of PDUs waiting to get transmitted. */
622 PISCSIPDUTX pIScsiPDUTxTail;
623 /** PDU we are currently transmitting. */
624 PISCSIPDUTX pIScsiPDUTxCur;
625 /** Number of commands waiting for an answer from the target.
626 * Used for timeout handling for poll.
627 */
628 unsigned cCmdsWaiting;
629 /** Table of commands waiting for a response from the target. */
630 PISCSICMD aCmdsWaiting[ISCSI_CMD_WAITING_ENTRIES];
631} ISCSIIMAGE;
632
633
634/*******************************************************************************
635* Static Variables *
636*******************************************************************************/
637
638/** Default initiator basename. */
639static const char *s_iscsiDefaultInitiatorBasename = "iqn.2009-08.com.sun.virtualbox.initiator";
640
641/** Default LUN. */
642static const char *s_iscsiConfigDefaultLUN = "0";
643
644/** Default timeout, 10 seconds. */
645static const char *s_iscsiConfigDefaultTimeout = "10000";
646
647/** Default write split value, less or equal to ISCSI_DATA_LENGTH_MAX. */
648static const char *s_iscsiConfigDefaultWriteSplit = "262144";
649
650/** Default host IP stack. */
651static const char *s_iscsiConfigDefaultHostIPStack = "1";
652
653/** Description of all accepted config parameters. */
654static const VDCONFIGINFO s_iscsiConfigInfo[] =
655{
656 { "TargetName", NULL, VDCFGVALUETYPE_STRING, VD_CFGKEY_MANDATORY },
657 /* LUN is defined of string type to handle the "enc" prefix. */
658 { "LUN", s_iscsiConfigDefaultLUN, VDCFGVALUETYPE_STRING, VD_CFGKEY_MANDATORY },
659 { "TargetAddress", NULL, VDCFGVALUETYPE_STRING, VD_CFGKEY_MANDATORY },
660 { "InitiatorName", NULL, VDCFGVALUETYPE_STRING, 0 },
661 { "InitiatorUsername", NULL, VDCFGVALUETYPE_STRING, 0 },
662 { "InitiatorSecret", NULL, VDCFGVALUETYPE_BYTES, 0 },
663 { "TargetUsername", NULL, VDCFGVALUETYPE_STRING, VD_CFGKEY_EXPERT },
664 { "TargetSecret", NULL, VDCFGVALUETYPE_BYTES, VD_CFGKEY_EXPERT },
665 { "WriteSplit", s_iscsiConfigDefaultWriteSplit, VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
666 { "Timeout", s_iscsiConfigDefaultTimeout, VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
667 { "HostIPStack", s_iscsiConfigDefaultHostIPStack, VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
668 { NULL, NULL, VDCFGVALUETYPE_INTEGER, 0 }
669};
670
671/*******************************************************************************
672* Internal Functions *
673*******************************************************************************/
674
675/* iSCSI low-level functions (only to be used from the iSCSI high-level functions). */
676static uint32_t iscsiNewITT(PISCSIIMAGE pImage);
677static int iscsiSendPDU(PISCSIIMAGE pImage, PISCSIREQ paReq, uint32_t cnReq, uint32_t uFlags);
678static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes);
679static int iscsiRecvPDUAsync(PISCSIIMAGE pImage);
680static int iscsiSendPDUAsync(PISCSIIMAGE pImage);
681static int iscsiValidatePDU(PISCSIRES paRes, uint32_t cnRes);
682static int iscsiRecvPDUProcess(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes);
683static int iscsiPDUTxPrepare(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd);
684static int iscsiRecvPDUUpdateRequest(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes);
685static void iscsiCmdComplete(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd, int rcCmd);
686static int iscsiTextAddKeyValue(uint8_t *pbBuf, size_t cbBuf, size_t *pcbBufCurr, const char *pcszKey, const char *pcszValue, size_t cbValue);
687static int iscsiTextGetKeyValue(const uint8_t *pbBuf, size_t cbBuf, const char *pcszKey, const char **ppcszValue);
688static int iscsiStrToBinary(const char *pcszValue, uint8_t *pbValue, size_t *pcbValue);
689static int iscsiUpdateParameters(PISCSIIMAGE pImage, const uint8_t *pbBuf, size_t cbBuf);
690
691/* Serial number arithmetic comparison. */
692static bool serial_number_less(uint32_t sn1, uint32_t sn2);
693static bool serial_number_greater(uint32_t sn1, uint32_t sn2);
694
695/* CHAP-MD5 functions. */
696#ifdef IMPLEMENT_TARGET_AUTH
697static void chap_md5_generate_challenge(uint8_t *pbChallenge, size_t *pcbChallenge);
698#endif
699static void chap_md5_compute_response(uint8_t *pbResponse, uint8_t id, const uint8_t *pbChallenge, size_t cbChallenge,
700 const uint8_t *pbSecret, size_t cbSecret);
701
702
703/**
704 * Internal: signal an error to the frontend.
705 */
706DECLINLINE(int) iscsiError(PISCSIIMAGE pImage, int rc, RT_SRC_POS_DECL,
707 const char *pszFormat, ...)
708{
709 va_list va;
710 va_start(va, pszFormat);
711 if (pImage->pInterfaceError)
712 pImage->pInterfaceErrorCallbacks->pfnError(pImage->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
713 pszFormat, va);
714 va_end(va);
715
716#ifdef LOG_ENABLED
717 va_start(va, pszFormat);
718 Log(("iscsiError(%d/%s): %N\n", iLine, pszFunction, pszFormat, &va));
719 va_end(va);
720#endif
721 return rc;
722}
723
724/**
725 * Internal: signal an informational message to the frontend.
726 */
727DECLINLINE(int) iscsiMessage(PISCSIIMAGE pImage, const char *pszFormat, ...)
728{
729 int rc = VINF_SUCCESS;
730 va_list va;
731 va_start(va, pszFormat);
732 if (pImage->pInterfaceError)
733 rc = pImage->pInterfaceErrorCallbacks->pfnMessage(pImage->pInterfaceError->pvUser,
734 pszFormat, va);
735 va_end(va);
736 return rc;
737}
738
739DECLINLINE(bool) iscsiIsClientConnected(PISCSIIMAGE pImage)
740{
741 return pImage->Socket != NIL_VDSOCKET
742 && pImage->pInterfaceNetCallbacks->pfnIsClientConnected(pImage->Socket);
743}
744
745/**
746 * Calculates the hash for the given ITT used
747 * to look up the command in the table.
748 */
749DECLINLINE(uint32_t) iscsiIttHash(uint32_t Itt)
750{
751 return Itt % ISCSI_CMD_WAITING_ENTRIES;
752}
753
754static PISCSICMD iscsiCmdGetFromItt(PISCSIIMAGE pImage, uint32_t Itt)
755{
756 PISCSICMD pIScsiCmd = NULL;
757
758 pIScsiCmd = pImage->aCmdsWaiting[iscsiIttHash(Itt)];
759
760 while ( pIScsiCmd
761 && pIScsiCmd->Itt != Itt)
762 pIScsiCmd = pIScsiCmd->pNext;
763
764 return pIScsiCmd;
765}
766
767static void iscsiCmdInsert(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd)
768{
769 PISCSICMD pIScsiCmdOld;
770 uint32_t idx = iscsiIttHash(pIScsiCmd->Itt);
771
772 Assert(!pIScsiCmd->pNext);
773
774 pIScsiCmdOld = pImage->aCmdsWaiting[idx];
775 pIScsiCmd->pNext = pIScsiCmdOld;
776 pImage->aCmdsWaiting[idx] = pIScsiCmd;
777 pImage->cCmdsWaiting++;
778}
779
780static PISCSICMD iscsiCmdRemove(PISCSIIMAGE pImage, uint32_t Itt)
781{
782 PISCSICMD pIScsiCmd = NULL;
783 PISCSICMD pIScsiCmdPrev = NULL;
784 uint32_t idx = iscsiIttHash(Itt);
785
786 pIScsiCmd = pImage->aCmdsWaiting[idx];
787
788 while ( pIScsiCmd
789 && pIScsiCmd->Itt != Itt)
790 {
791 pIScsiCmdPrev = pIScsiCmd;
792 pIScsiCmd = pIScsiCmd->pNext;
793 }
794
795 if (pIScsiCmd)
796 {
797 if (pIScsiCmdPrev)
798 {
799 Assert(!pIScsiCmd->pNext || VALID_PTR(pIScsiCmd->pNext));
800 pIScsiCmdPrev->pNext = pIScsiCmd->pNext;
801 }
802 else
803 {
804 pImage->aCmdsWaiting[idx] = pIScsiCmd->pNext;
805 Assert(!pImage->aCmdsWaiting[idx] || VALID_PTR(pImage->aCmdsWaiting[idx]));
806 }
807 pImage->cCmdsWaiting--;
808 }
809
810 return pIScsiCmd;
811}
812
813/**
814 * Removes all commands from the table and returns the
815 * list head
816 *
817 * @returns Pointer to the head of teh command list.
818 * @param pImage iSCSI connection to use.
819 */
820static PISCSICMD iscsiCmdRemoveAll(PISCSIIMAGE pImage)
821{
822 PISCSICMD pIScsiCmdHead = NULL;
823
824 for (unsigned idx = 0; idx < RT_ELEMENTS(pImage->aCmdsWaiting); idx++)
825 {
826 PISCSICMD pHead;
827 PISCSICMD pTail;
828
829 pHead = pImage->aCmdsWaiting[idx];
830 pImage->aCmdsWaiting[idx] = NULL;
831
832 if (pHead)
833 {
834 /* Get the tail. */
835 pTail = pHead;
836 while (pTail->pNext)
837 pTail = pTail->pNext;
838
839 /* Concatenate. */
840 pTail->pNext = pIScsiCmdHead;
841 pIScsiCmdHead = pHead;
842 }
843 }
844
845 return pIScsiCmdHead;
846}
847
848static int iscsiTransportConnect(PISCSIIMAGE pImage)
849{
850 int rc;
851 if (!pImage->pszHostname)
852 return VERR_NET_DEST_ADDRESS_REQUIRED;
853
854 rc = pImage->pInterfaceNetCallbacks->pfnClientConnect(pImage->Socket, pImage->pszHostname, pImage->uPort);
855 if (RT_FAILURE(rc))
856 {
857 if ( rc == VERR_NET_CONNECTION_REFUSED
858 || rc == VERR_NET_CONNECTION_RESET
859 || rc == VERR_NET_UNREACHABLE
860 || rc == VERR_NET_HOST_UNREACHABLE
861 || rc == VERR_NET_CONNECTION_TIMED_OUT)
862 {
863 /* Standardize return value for no connection. */
864 rc = VERR_NET_CONNECTION_REFUSED;
865 }
866 return rc;
867 }
868
869 /* Disable Nagle algorithm, we want things to be sent immediately. */
870 pImage->pInterfaceNetCallbacks->pfnSetSendCoalescing(pImage->Socket, false);
871
872 /* Make initiator name and ISID unique on this host. */
873 RTNETADDR LocalAddr;
874 rc = pImage->pInterfaceNetCallbacks->pfnGetLocalAddress(pImage->Socket,
875 &LocalAddr);
876 if (RT_FAILURE(rc))
877 return rc;
878 if ( LocalAddr.uPort == RTNETADDR_PORT_NA
879 || LocalAddr.uPort > 65535)
880 return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
881 pImage->ISID &= ~65535ULL;
882 pImage->ISID |= LocalAddr.uPort;
883 /* Eliminate the port so that it isn't included below. */
884 LocalAddr.uPort = RTNETADDR_PORT_NA;
885 if (pImage->fAutomaticInitiatorName)
886 {
887 if (pImage->pszInitiatorName)
888 RTStrFree(pImage->pszInitiatorName);
889 RTStrAPrintf(&pImage->pszInitiatorName, "%s:01:%RTnaddr",
890 s_iscsiDefaultInitiatorBasename, &LocalAddr);
891 if (!pImage->pszInitiatorName)
892 return VERR_NO_MEMORY;
893 }
894 return VINF_SUCCESS;
895}
896
897
898static int iscsiTransportRead(PISCSIIMAGE pImage, PISCSIRES paResponse, unsigned int cnResponse)
899{
900 int rc = VINF_SUCCESS;
901 unsigned int i = 0;
902 size_t cbToRead, cbActuallyRead, residual, cbSegActual = 0, cbAHSLength, cbDataLength;
903 char *pDst;
904
905 LogFlowFunc(("cnResponse=%d (%s:%d)\n", cnResponse, pImage->pszHostname, pImage->uPort));
906 if (!iscsiIsClientConnected(pImage))
907 {
908 /* Reconnecting makes no sense in this case, as there will be nothing
909 * to receive. We would just run into a timeout. */
910 rc = VERR_BROKEN_PIPE;
911 }
912
913 if (RT_SUCCESS(rc) && paResponse[0].cbSeg >= ISCSI_BHS_SIZE)
914 {
915 cbToRead = 0;
916 residual = ISCSI_BHS_SIZE; /* Do not read more than the BHS length before the true PDU length is known. */
917 cbSegActual = residual;
918 pDst = (char *)paResponse[i].pvSeg;
919 uint64_t u64Timeout = RTTimeMilliTS() + pImage->uReadTimeout;
920 do
921 {
922 int64_t cMilliesRemaining = u64Timeout - RTTimeMilliTS();
923 if (cMilliesRemaining <= 0)
924 {
925 rc = VERR_TIMEOUT;
926 break;
927 }
928 Assert(cMilliesRemaining < 1000000);
929 rc = pImage->pInterfaceNetCallbacks->pfnSelectOne(pImage->Socket,
930 cMilliesRemaining);
931 if (RT_FAILURE(rc))
932 break;
933 rc = pImage->pInterfaceNetCallbacks->pfnRead(pImage->Socket,
934 pDst, residual,
935 &cbActuallyRead);
936 if (RT_FAILURE(rc))
937 break;
938 if (cbActuallyRead == 0)
939 {
940 /* The other end has closed the connection. */
941 pImage->pInterfaceNetCallbacks->pfnClientClose(pImage->Socket);
942 pImage->state = ISCSISTATE_FREE;
943 rc = VERR_NET_CONNECTION_RESET;
944 break;
945 }
946 if (cbToRead == 0)
947 {
948 /* Currently reading the BHS. */
949 residual -= cbActuallyRead;
950 pDst += cbActuallyRead;
951 if (residual <= 40)
952 {
953 /* Enough data read to figure out the actual PDU size. */
954 uint32_t word1 = RT_N2H_U32(((uint32_t *)(paResponse[0].pvSeg))[1]);
955 cbAHSLength = (word1 & 0xff000000) >> 24;
956 cbAHSLength = ((cbAHSLength - 1) | 3) + 1; /* Add padding. */
957 cbDataLength = word1 & 0x00ffffff;
958 cbDataLength = ((cbDataLength - 1) | 3) + 1; /* Add padding. */
959 cbToRead = residual + cbAHSLength + cbDataLength;
960 residual += paResponse[0].cbSeg - ISCSI_BHS_SIZE;
961 if (residual > cbToRead)
962 residual = cbToRead;
963 cbSegActual = ISCSI_BHS_SIZE + cbAHSLength + cbDataLength;
964 /* Check whether we are already done with this PDU (no payload). */
965 if (cbToRead == 0)
966 break;
967 }
968 }
969 else
970 {
971 cbToRead -= cbActuallyRead;
972 if (cbToRead == 0)
973 break;
974 pDst += cbActuallyRead;
975 residual -= cbActuallyRead;
976 }
977 if (residual == 0)
978 {
979 i++;
980 if (i >= cnResponse)
981 {
982 /* No space left in receive buffers. */
983 rc = VERR_BUFFER_OVERFLOW;
984 break;
985 }
986 pDst = (char *)paResponse[i].pvSeg;
987 residual = paResponse[i].cbSeg;
988 if (residual > cbToRead)
989 residual = cbToRead;
990 cbSegActual = residual;
991 }
992 LogFlowFunc(("cbToRead=%u residual=%u cbSegActual=%u cbActuallRead=%u\n",
993 cbToRead, residual, cbSegActual, cbActuallyRead));
994 } while (true);
995 }
996 else
997 {
998 if (RT_SUCCESS(rc))
999 rc = VERR_BUFFER_OVERFLOW;
1000 }
1001 if (RT_SUCCESS(rc))
1002 {
1003 paResponse[i].cbSeg = cbSegActual;
1004 for (i++; i < cnResponse; i++)
1005 paResponse[i].cbSeg = 0;
1006 }
1007
1008 if (RT_UNLIKELY( RT_FAILURE(rc)
1009 && ( rc == VERR_NET_CONNECTION_RESET
1010 || rc == VERR_NET_CONNECTION_ABORTED
1011 || rc == VERR_NET_CONNECTION_RESET_BY_PEER
1012 || rc == VERR_NET_CONNECTION_REFUSED
1013 || rc == VERR_BROKEN_PIPE)))
1014 {
1015 /* Standardize return value for broken connection. */
1016 rc = VERR_BROKEN_PIPE;
1017 }
1018
1019 LogFlowFunc(("returns %Rrc\n", rc));
1020 return rc;
1021}
1022
1023
1024static int iscsiTransportWrite(PISCSIIMAGE pImage, PISCSIREQ paRequest, unsigned int cnRequest)
1025{
1026 int rc = VINF_SUCCESS;
1027 uint32_t pad = 0;
1028 unsigned int i;
1029
1030 LogFlowFunc(("cnRequest=%d (%s:%d)\n", cnRequest, pImage->pszHostname, pImage->uPort));
1031 if (!iscsiIsClientConnected(pImage))
1032 {
1033 /* Attempt to reconnect if the connection was previously broken. */
1034 rc = iscsiTransportConnect(pImage);
1035 }
1036
1037 if (RT_SUCCESS(rc))
1038 {
1039 /* Construct scatter/gather buffer for entire request, worst case
1040 * needs twice as many entries to allow for padding. */
1041 unsigned cBuf = 0;
1042 for (i = 0; i < cnRequest; i++)
1043 {
1044 cBuf++;
1045 if (paRequest[i].cbSeg & 3)
1046 cBuf++;
1047 }
1048 Assert(cBuf < ISCSI_SG_SEGMENTS_MAX);
1049 RTSGBUF buf;
1050 RTSGSEG aSeg[ISCSI_SG_SEGMENTS_MAX];
1051 static char aPad[4] = { 0, 0, 0, 0 };
1052 RTSgBufInit(&buf, &aSeg[0], cBuf);
1053 unsigned iBuf = 0;
1054 for (i = 0; i < cnRequest; i++)
1055 {
1056 /* Actual data chunk. */
1057 aSeg[iBuf].pvSeg = (void *)paRequest[i].pcvSeg;
1058 aSeg[iBuf].cbSeg = paRequest[i].cbSeg;
1059 iBuf++;
1060 /* Insert proper padding before the next chunk. */
1061 if (paRequest[i].cbSeg & 3)
1062 {
1063 aSeg[iBuf].pvSeg = &aPad[0];
1064 aSeg[iBuf].cbSeg = 4 - (paRequest[i].cbSeg & 3);
1065 iBuf++;
1066 }
1067 }
1068 /* Send out the request, the socket is set to send data immediately,
1069 * avoiding unnecessary delays. */
1070 rc = pImage->pInterfaceNetCallbacks->pfnSgWrite(pImage->Socket, &buf);
1071
1072 }
1073
1074 if (RT_UNLIKELY( RT_FAILURE(rc)
1075 && ( rc == VERR_NET_CONNECTION_RESET
1076 || rc == VERR_NET_CONNECTION_ABORTED
1077 || rc == VERR_NET_CONNECTION_RESET_BY_PEER
1078 || rc == VERR_NET_CONNECTION_REFUSED
1079 || rc == VERR_BROKEN_PIPE)))
1080 {
1081 /* Standardize return value for broken connection. */
1082 rc = VERR_BROKEN_PIPE;
1083 }
1084
1085 LogFlowFunc(("returns %Rrc\n", rc));
1086 return rc;
1087}
1088
1089
1090static int iscsiTransportOpen(PISCSIIMAGE pImage)
1091{
1092 int rc = VINF_SUCCESS;
1093 size_t cbHostname = 0; /* shut up gcc */
1094 const char *pcszPort = NULL; /* shut up gcc */
1095 char *pszPortEnd;
1096 uint16_t uPort;
1097
1098 /* Clean up previous connection data. */
1099 if (iscsiIsClientConnected(pImage))
1100 {
1101 pImage->pInterfaceNetCallbacks->pfnClientClose(pImage->Socket);
1102 }
1103 if (pImage->pszHostname)
1104 {
1105 RTMemFree(pImage->pszHostname);
1106 pImage->pszHostname = NULL;
1107 pImage->uPort = 0;
1108 }
1109
1110 /* Locate the port number via the colon separating the hostname from the port. */
1111 if (*pImage->pszTargetAddress)
1112 {
1113 if (*pImage->pszTargetAddress != '[')
1114 {
1115 /* Normal hostname or IPv4 dotted decimal. */
1116 pcszPort = strchr(pImage->pszTargetAddress, ':');
1117 if (pcszPort != NULL)
1118 {
1119 cbHostname = pcszPort - pImage->pszTargetAddress;
1120 pcszPort++;
1121 }
1122 else
1123 cbHostname = strlen(pImage->pszTargetAddress);
1124 }
1125 else
1126 {
1127 /* IPv6 literal address. Contains colons, so skip to closing square bracket. */
1128 pcszPort = strchr(pImage->pszTargetAddress, ']');
1129 if (pcszPort != NULL)
1130 {
1131 pcszPort++;
1132 cbHostname = pcszPort - pImage->pszTargetAddress;
1133 if (*pcszPort == '\0')
1134 pcszPort = NULL;
1135 else if (*pcszPort != ':')
1136 rc = VERR_PARSE_ERROR;
1137 else
1138 pcszPort++;
1139 }
1140 else
1141 rc = VERR_PARSE_ERROR;
1142 }
1143 }
1144 else
1145 rc = VERR_PARSE_ERROR;
1146
1147 /* Now split address into hostname and port. */
1148 if (RT_SUCCESS(rc))
1149 {
1150 pImage->pszHostname = (char *)RTMemAlloc(cbHostname + 1);
1151 if (!pImage->pszHostname)
1152 rc = VERR_NO_MEMORY;
1153 else
1154 {
1155 memcpy(pImage->pszHostname, pImage->pszTargetAddress, cbHostname);
1156 pImage->pszHostname[cbHostname] = '\0';
1157 if (pcszPort != NULL)
1158 {
1159 rc = RTStrToUInt16Ex(pcszPort, &pszPortEnd, 0, &uPort);
1160 /* Note that RT_SUCCESS() macro to check the rc value is not strict enough in this case. */
1161 if (rc == VINF_SUCCESS && *pszPortEnd == '\0' && uPort != 0)
1162 {
1163 pImage->uPort = uPort;
1164 }
1165 else
1166 {
1167 rc = VERR_PARSE_ERROR;
1168 }
1169 }
1170 else
1171 pImage->uPort = ISCSI_DEFAULT_PORT;
1172 }
1173 }
1174
1175 if (RT_SUCCESS(rc))
1176 {
1177 if (!iscsiIsClientConnected(pImage))
1178 rc = iscsiTransportConnect(pImage);
1179 }
1180 else
1181 {
1182 if (pImage->pszHostname)
1183 {
1184 RTMemFree(pImage->pszHostname);
1185 pImage->pszHostname = NULL;
1186 }
1187 pImage->uPort = 0;
1188 }
1189
1190 LogFlowFunc(("returns %Rrc\n", rc));
1191 return rc;
1192}
1193
1194
1195static int iscsiTransportClose(PISCSIIMAGE pImage)
1196{
1197 int rc;
1198
1199 LogFlowFunc(("(%s:%d)\n", pImage->pszHostname, pImage->uPort));
1200 if (iscsiIsClientConnected(pImage))
1201 {
1202 rc = pImage->pInterfaceNetCallbacks->pfnClientClose(pImage->Socket);
1203 }
1204 else
1205 rc = VINF_SUCCESS;
1206 LogFlowFunc(("returns %Rrc\n", rc));
1207 return rc;
1208}
1209
1210
1211/**
1212 * Attach to an iSCSI target. Performs all operations necessary to enter
1213 * Full Feature Phase.
1214 *
1215 * @returns VBox status.
1216 * @param pImage The iSCSI connection state to be used.
1217 */
1218static int iscsiAttach(void *pvUser)
1219{
1220 int rc;
1221 uint32_t itt;
1222 uint32_t csg, nsg, substate;
1223 uint64_t isid_tsih;
1224 uint8_t bBuf[4096]; /* Should be large enough even for large authentication values. */
1225 size_t cbBuf;
1226 bool transit;
1227 uint8_t pbChallenge[1024]; /* RFC3720 specifies this as maximum. */
1228 size_t cbChallenge = 0; /* shut up gcc */
1229 uint8_t bChapIdx;
1230 uint8_t aResponse[RTMD5HASHSIZE];
1231 uint32_t cnISCSIReq;
1232 ISCSIREQ aISCSIReq[4];
1233 uint32_t aReqBHS[12];
1234 uint32_t cnISCSIRes;
1235 ISCSIRES aISCSIRes[2];
1236 uint32_t aResBHS[12];
1237 char *pszNext;
1238 PISCSIIMAGE pImage = (PISCSIIMAGE)pvUser;
1239
1240 bool fParameterNeg = true;;
1241 pImage->cbRecvDataLength = ISCSI_DATA_LENGTH_MAX;
1242 pImage->cbSendDataLength = RT_MIN(ISCSI_DATA_LENGTH_MAX, pImage->cbWriteSplit);
1243 char szMaxDataLength[16];
1244 RTStrPrintf(szMaxDataLength, sizeof(szMaxDataLength), "%u", ISCSI_DATA_LENGTH_MAX);
1245 ISCSIPARAMETER aParameterNeg[] =
1246 {
1247 { "HeaderDigest", "None", 0 },
1248 { "DataDigest", "None", 0 },
1249 { "MaxConnections", "1", 0 },
1250 { "InitialR2T", "No", 0 },
1251 { "ImmediateData", "Yes", 0 },
1252 { "MaxRecvDataSegmentLength", szMaxDataLength, 0 },
1253 { "MaxBurstLength", szMaxDataLength, 0 },
1254 { "FirstBurstLength", szMaxDataLength, 0 },
1255 { "DefaultTime2Wait", "0", 0 },
1256 { "DefaultTime2Retain", "60", 0 },
1257 { "DataPDUInOrder", "Yes", 0 },
1258 { "DataSequenceInOrder", "Yes", 0 },
1259 { "ErrorRecoveryLevel", "0", 0 },
1260 { "MaxOutstandingR2T", "1", 0 }
1261 };
1262
1263 LogFlowFunc(("entering\n"));
1264
1265 Assert(pImage->state == ISCSISTATE_FREE);
1266
1267 RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
1268
1269 /* Make 100% sure the connection isn't reused for a new login. */
1270 iscsiTransportClose(pImage);
1271
1272restart:
1273 if (!iscsiIsClientConnected(pImage))
1274 {
1275 rc = iscsiTransportOpen(pImage);
1276 if (RT_FAILURE(rc))
1277 goto out;
1278 }
1279
1280 pImage->state = ISCSISTATE_IN_LOGIN;
1281 pImage->ITT = 1;
1282 pImage->FirstRecvPDU = true;
1283 pImage->CmdSN = 1;
1284 pImage->ExpCmdSN = 0;
1285 pImage->MaxCmdSN = 1;
1286 pImage->ExpStatSN = 0;
1287
1288 /*
1289 * Send login request to target.
1290 */
1291 itt = iscsiNewITT(pImage);
1292 csg = 0;
1293 nsg = 0;
1294 substate = 0;
1295 isid_tsih = pImage->ISID << 16; /* TSIH field currently always 0 */
1296
1297 do {
1298 transit = false;
1299 cbBuf = 0;
1300 /* Handle all cases with a single switch statement. */
1301 switch (csg << 8 | substate)
1302 {
1303 case 0x0000: /* security negotiation, step 0: propose authentication. */
1304 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "SessionType", "Normal", 0);
1305 if (RT_FAILURE(rc))
1306 goto out;
1307 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "InitiatorName", pImage->pszInitiatorName, 0);
1308 if (RT_FAILURE(rc))
1309 goto out;
1310 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "TargetName", pImage->pszTargetName, 0);
1311 if (RT_FAILURE(rc))
1312 goto out;
1313 if (pImage->pszInitiatorUsername == NULL)
1314 {
1315 /* No authentication. Immediately switch to next phase. */
1316 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "AuthMethod", "None", 0);
1317 if (RT_FAILURE(rc))
1318 goto out;
1319 nsg = 1;
1320 transit = true;
1321 }
1322 else
1323 {
1324 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "AuthMethod", "CHAP,None", 0);
1325 if (RT_FAILURE(rc))
1326 goto out;
1327 }
1328 break;
1329 case 0x0001: /* security negotiation, step 1: propose CHAP_MD5 variant. */
1330 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_A", "5", 0);
1331 if (RT_FAILURE(rc))
1332 goto out;
1333 break;
1334 case 0x0002: /* security negotiation, step 2: send authentication info. */
1335 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_N", pImage->pszInitiatorUsername, 0);
1336 if (RT_FAILURE(rc))
1337 goto out;
1338 chap_md5_compute_response(aResponse, bChapIdx, pbChallenge, cbChallenge,
1339 pImage->pbInitiatorSecret, pImage->cbInitiatorSecret);
1340 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_R", (const char *)aResponse, RTMD5HASHSIZE);
1341 if (RT_FAILURE(rc))
1342 goto out;
1343 nsg = 1;
1344 transit = true;
1345 break;
1346 case 0x0100: /* login operational negotiation, step 0: set parameters. */
1347 if (fParameterNeg)
1348 {
1349 for (unsigned i = 0; i < RT_ELEMENTS(aParameterNeg); i++)
1350 {
1351 rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf,
1352 aParameterNeg[i].pszParamName,
1353 aParameterNeg[i].pszParamValue,
1354 aParameterNeg[i].cbParamValue);
1355 if (RT_FAILURE(rc))
1356 goto out;
1357 }
1358 fParameterNeg = false;
1359 }
1360
1361 nsg = 3;
1362 transit = true;
1363 break;
1364 case 0x0300: /* full feature phase. */
1365 default:
1366 /* Should never come here. */
1367 AssertMsgFailed(("send: Undefined login state %d substate %d\n", csg, substate));
1368 break;
1369 }
1370
1371 aReqBHS[0] = RT_H2N_U32( ISCSI_IMMEDIATE_DELIVERY_BIT
1372 | (csg << ISCSI_CSG_SHIFT)
1373 | (transit ? (nsg << ISCSI_NSG_SHIFT | ISCSI_TRANSIT_BIT) : 0)
1374 | ISCSI_MY_VERSION /* Minimum version. */
1375 | (ISCSI_MY_VERSION << 8) /* Maximum version. */
1376 | ISCSIOP_LOGIN_REQ); /* C=0 */
1377 aReqBHS[1] = RT_H2N_U32((uint32_t)cbBuf); /* TotalAHSLength=0 */
1378 aReqBHS[2] = RT_H2N_U32(isid_tsih >> 32);
1379 aReqBHS[3] = RT_H2N_U32(isid_tsih & 0xffffffff);
1380 aReqBHS[4] = itt;
1381 aReqBHS[5] = RT_H2N_U32(1 << 16); /* CID=1,reserved */
1382 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
1383 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
1384 aReqBHS[8] = 0; /* reserved */
1385 aReqBHS[9] = 0; /* reserved */
1386 aReqBHS[10] = 0; /* reserved */
1387 aReqBHS[11] = 0; /* reserved */
1388
1389 cnISCSIReq = 0;
1390 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
1391 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
1392 cnISCSIReq++;
1393
1394 aISCSIReq[cnISCSIReq].pcvSeg = bBuf;
1395 aISCSIReq[cnISCSIReq].cbSeg = cbBuf;
1396 cnISCSIReq++;
1397
1398 rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_NO_REATTACH);
1399 if (RT_SUCCESS(rc))
1400 {
1401 ISCSIOPCODE cmd;
1402 ISCSILOGINSTATUSCLASS loginStatusClass;
1403
1404 cnISCSIRes = 0;
1405 aISCSIRes[cnISCSIRes].pvSeg = aResBHS;
1406 aISCSIRes[cnISCSIRes].cbSeg = sizeof(aResBHS);
1407 cnISCSIRes++;
1408 aISCSIRes[cnISCSIRes].pvSeg = bBuf;
1409 aISCSIRes[cnISCSIRes].cbSeg = sizeof(bBuf);
1410 cnISCSIRes++;
1411
1412 rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes);
1413 if (RT_FAILURE(rc))
1414 break;
1415 /** @todo collect partial login responses with Continue bit set. */
1416 Assert(aISCSIRes[0].pvSeg == aResBHS);
1417 Assert(aISCSIRes[0].cbSeg >= ISCSI_BHS_SIZE);
1418 Assert((RT_N2H_U32(aResBHS[0]) & ISCSI_CONTINUE_BIT) == 0);
1419
1420 cmd = (ISCSIOPCODE)(RT_N2H_U32(aResBHS[0]) & ISCSIOP_MASK);
1421 if (cmd == ISCSIOP_LOGIN_RES)
1422 {
1423 if ((RT_N2H_U32(aResBHS[0]) & 0xff) != ISCSI_MY_VERSION)
1424 {
1425 iscsiTransportClose(pImage);
1426 rc = VERR_PARSE_ERROR;
1427 break; /* Give up immediately, as a RFC violation in version fields is very serious. */
1428 }
1429
1430 loginStatusClass = (ISCSILOGINSTATUSCLASS)(RT_N2H_U32(aResBHS[9]) >> 24);
1431 switch (loginStatusClass)
1432 {
1433 case ISCSI_LOGIN_STATUS_CLASS_SUCCESS:
1434 uint32_t targetCSG;
1435 uint32_t targetNSG;
1436 bool targetTransit;
1437
1438 if (pImage->FirstRecvPDU)
1439 {
1440 pImage->FirstRecvPDU = false;
1441 pImage->ExpStatSN = RT_N2H_U32(aResBHS[6]) + 1;
1442 }
1443
1444 targetCSG = (RT_N2H_U32(aResBHS[0]) & ISCSI_CSG_MASK) >> ISCSI_CSG_SHIFT;
1445 targetNSG = (RT_N2H_U32(aResBHS[0]) & ISCSI_NSG_MASK) >> ISCSI_NSG_SHIFT;
1446 targetTransit = !!(RT_N2H_U32(aResBHS[0]) & ISCSI_TRANSIT_BIT);
1447
1448 /* Handle all cases with a single switch statement. */
1449 switch (csg << 8 | substate)
1450 {
1451 case 0x0000: /* security negotiation, step 0: receive final authentication. */
1452 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1453 if (RT_FAILURE(rc))
1454 break;
1455
1456 const char *pcszAuthMethod;
1457
1458 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "AuthMethod", &pcszAuthMethod);
1459 if (RT_FAILURE(rc))
1460 {
1461 rc = VERR_PARSE_ERROR;
1462 break;
1463 }
1464 if (strcmp(pcszAuthMethod, "None") == 0)
1465 {
1466 /* Authentication offered, but none required. Skip to operational parameters. */
1467 csg = 1;
1468 nsg = 1;
1469 transit = true;
1470 substate = 0;
1471 break;
1472 }
1473 else if (strcmp(pcszAuthMethod, "CHAP") == 0 && targetNSG == 0 && !targetTransit)
1474 {
1475 /* CHAP authentication required, continue with next substate. */
1476 substate++;
1477 break;
1478 }
1479
1480 /* Unknown auth method or login response PDU headers incorrect. */
1481 rc = VERR_PARSE_ERROR;
1482 break;
1483 case 0x0001: /* security negotiation, step 1: receive final CHAP variant and challenge. */
1484 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1485 if (RT_FAILURE(rc))
1486 break;
1487
1488 const char *pcszChapAuthMethod;
1489 const char *pcszChapIdxTarget;
1490 const char *pcszChapChallengeStr;
1491
1492 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_A", &pcszChapAuthMethod);
1493 if (RT_FAILURE(rc))
1494 {
1495 rc = VERR_PARSE_ERROR;
1496 break;
1497 }
1498 if (strcmp(pcszChapAuthMethod, "5") != 0)
1499 {
1500 rc = VERR_PARSE_ERROR;
1501 break;
1502 }
1503 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_I", &pcszChapIdxTarget);
1504 if (RT_FAILURE(rc))
1505 {
1506 rc = VERR_PARSE_ERROR;
1507 break;
1508 }
1509 rc = RTStrToUInt8Ex(pcszChapIdxTarget, &pszNext, 0, &bChapIdx);
1510 if ((rc > VINF_SUCCESS) || *pszNext != '\0')
1511 {
1512 rc = VERR_PARSE_ERROR;
1513 break;
1514 }
1515 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_C", &pcszChapChallengeStr);
1516 if (RT_FAILURE(rc))
1517 {
1518 rc = VERR_PARSE_ERROR;
1519 break;
1520 }
1521 cbChallenge = sizeof(pbChallenge);
1522 rc = iscsiStrToBinary(pcszChapChallengeStr, pbChallenge, &cbChallenge);
1523 if (RT_FAILURE(rc))
1524 break;
1525 substate++;
1526 transit = true;
1527 break;
1528 case 0x0002: /* security negotiation, step 2: check authentication success. */
1529 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1530 if (RT_FAILURE(rc))
1531 break;
1532
1533 if (targetCSG == 0 && targetNSG == 1 && targetTransit)
1534 {
1535 /* Target wants to continue in login operational state, authentication success. */
1536 csg = 1;
1537 nsg = 3;
1538 substate = 0;
1539 break;
1540 }
1541 rc = VERR_PARSE_ERROR;
1542 break;
1543 case 0x0100: /* login operational negotiation, step 0: check results. */
1544 rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
1545 if (RT_FAILURE(rc))
1546 break;
1547
1548 if (targetCSG == 1 && targetNSG == 3 && targetTransit)
1549 {
1550 /* Target wants to continue in full feature phase, login finished. */
1551 csg = 3;
1552 nsg = 3;
1553 substate = 0;
1554 break;
1555 }
1556 else if (targetCSG == 1 && targetNSG == 1 && !targetTransit)
1557 {
1558 /* Target wants to negotiate certain parameters and
1559 * stay in login operational negotiation. */
1560 csg = 1;
1561 nsg = 3;
1562 substate = 0;
1563 }
1564 rc = VERR_PARSE_ERROR;
1565 break;
1566 case 0x0300: /* full feature phase. */
1567 default:
1568 AssertMsgFailed(("recv: Undefined login state %d substate %d\n", csg, substate));
1569 rc = VERR_PARSE_ERROR;
1570 break;
1571 }
1572 break;
1573 case ISCSI_LOGIN_STATUS_CLASS_REDIRECTION:
1574 const char *pcszTargetRedir;
1575
1576 /* Target has moved to some other location, as indicated in the TargetAddress key. */
1577 rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "TargetAddress", &pcszTargetRedir);
1578 if (RT_FAILURE(rc))
1579 {
1580 rc = VERR_PARSE_ERROR;
1581 break;
1582 }
1583 if (pImage->pszTargetAddress)
1584 RTMemFree(pImage->pszTargetAddress);
1585 {
1586 size_t cb = strlen(pcszTargetRedir) + 1;
1587 pImage->pszTargetAddress = (char *)RTMemAlloc(cb);
1588 if (!pImage->pszTargetAddress)
1589 {
1590 rc = VERR_NO_MEMORY;
1591 break;
1592 }
1593 memcpy(pImage->pszTargetAddress, pcszTargetRedir, cb);
1594 }
1595 rc = iscsiTransportOpen(pImage);
1596 goto restart;
1597 case ISCSI_LOGIN_STATUS_CLASS_INITIATOR_ERROR:
1598 iscsiTransportClose(pImage);
1599 rc = VERR_IO_GEN_FAILURE;
1600 goto out;
1601 case ISCSI_LOGIN_STATUS_CLASS_TARGET_ERROR:
1602 iscsiTransportClose(pImage);
1603 rc = VINF_EOF;
1604 break;
1605 default:
1606 rc = VERR_PARSE_ERROR;
1607 }
1608
1609 if (csg == 3)
1610 {
1611 /*
1612 * Finished login, continuing with Full Feature Phase.
1613 */
1614 rc = VINF_SUCCESS;
1615 break;
1616 }
1617 }
1618 else
1619 {
1620 AssertMsgFailed(("%s: ignoring unexpected PDU with first word = %#08x\n", __FUNCTION__, RT_N2H_U32(aResBHS[0])));
1621 }
1622 }
1623 else
1624 break;
1625 } while (true);
1626
1627out:
1628 if (RT_FAILURE(rc))
1629 {
1630 /*
1631 * Close connection to target.
1632 */
1633 iscsiTransportClose(pImage);
1634 pImage->state = ISCSISTATE_FREE;
1635 }
1636 else
1637 pImage->state = ISCSISTATE_NORMAL;
1638
1639 RTSemMutexRelease(pImage->Mutex);
1640
1641 LogFlowFunc(("returning %Rrc\n", rc));
1642 LogRel(("iSCSI: login to target %s %s\n", pImage->pszTargetName, RT_SUCCESS(rc) ? "successful" : "failed"));
1643 return rc;
1644}
1645
1646
1647/**
1648 * Detach from an iSCSI target.
1649 *
1650 * @returns VBox status.
1651 * @param pImage The iSCSI connection state to be used.
1652 */
1653static int iscsiDetach(void *pvUser)
1654{
1655 int rc;
1656 uint32_t itt;
1657 uint32_t cnISCSIReq = 0;
1658 ISCSIREQ aISCSIReq[4];
1659 uint32_t aReqBHS[12];
1660 PISCSIIMAGE pImage = (PISCSIIMAGE)pvUser;
1661
1662 LogFlowFunc(("entering\n"));
1663
1664 RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
1665
1666 if (pImage->state != ISCSISTATE_FREE && pImage->state != ISCSISTATE_IN_LOGOUT)
1667 {
1668 pImage->state = ISCSISTATE_IN_LOGOUT;
1669
1670 /*
1671 * Send logout request to target.
1672 */
1673 itt = iscsiNewITT(pImage);
1674 aReqBHS[0] = RT_H2N_U32(ISCSI_FINAL_BIT | ISCSIOP_LOGOUT_REQ); /* I=0,F=1,Reason=close session */
1675 aReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
1676 aReqBHS[2] = 0; /* reserved */
1677 aReqBHS[3] = 0; /* reserved */
1678 aReqBHS[4] = itt;
1679 aReqBHS[5] = 0; /* reserved */
1680 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
1681 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
1682 aReqBHS[8] = 0; /* reserved */
1683 aReqBHS[9] = 0; /* reserved */
1684 aReqBHS[10] = 0; /* reserved */
1685 aReqBHS[11] = 0; /* reserved */
1686 pImage->CmdSN++;
1687
1688 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
1689 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
1690 cnISCSIReq++;
1691
1692 rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_NO_REATTACH);
1693 if (RT_SUCCESS(rc))
1694 {
1695 /*
1696 * Read logout response from target.
1697 */
1698 ISCSIRES aISCSIRes;
1699 uint32_t aResBHS[12];
1700
1701 aISCSIRes.pvSeg = aResBHS;
1702 aISCSIRes.cbSeg = sizeof(aResBHS);
1703 rc = iscsiRecvPDU(pImage, itt, &aISCSIRes, 1);
1704 if (RT_SUCCESS(rc))
1705 {
1706 if (RT_N2H_U32(aResBHS[0]) != (ISCSI_FINAL_BIT | ISCSIOP_LOGOUT_RES))
1707 AssertMsgFailed(("iSCSI Logout response invalid\n"));
1708 }
1709 else
1710 AssertMsgFailed(("iSCSI Logout response error, rc=%Rrc\n", rc));
1711 }
1712 else
1713 AssertMsgFailed(("Could not send iSCSI Logout request, rc=%Rrc\n", rc));
1714 }
1715
1716 if (pImage->state != ISCSISTATE_FREE)
1717 {
1718 /*
1719 * Close connection to target.
1720 */
1721 rc = iscsiTransportClose(pImage);
1722 if (RT_FAILURE(rc))
1723 AssertMsgFailed(("Could not close connection to target, rc=%Rrc\n", rc));
1724 }
1725
1726 pImage->state = ISCSISTATE_FREE;
1727
1728 RTSemMutexRelease(pImage->Mutex);
1729
1730 LogFlowFunc(("leaving\n"));
1731 LogRel(("iSCSI: logout to target %s\n", pImage->pszTargetName));
1732 return VINF_SUCCESS;
1733}
1734
1735
1736/**
1737 * Perform a command on an iSCSI target. Target must be already in
1738 * Full Feature Phase.
1739 *
1740 * @returns VBOX status.
1741 * @param pImage The iSCSI connection state to be used.
1742 * @param pRequest Command descriptor. Contains all information about
1743 * the command, its transfer directions and pointers
1744 * to the buffer(s) used for transferring data and
1745 * status information.
1746 */
1747static int iscsiCommand(PISCSIIMAGE pImage, PSCSIREQ pRequest)
1748{
1749 int rc;
1750 uint32_t itt;
1751 uint32_t cbData;
1752 uint32_t cnISCSIReq = 0;
1753 ISCSIREQ aISCSIReq[4];
1754 uint32_t aReqBHS[12];
1755
1756 uint32_t *pDst = NULL;
1757 size_t cbBufLength;
1758 uint32_t aStatus[256]; /**< Plenty of buffer for status information. */
1759 uint32_t ExpDataSN = 0;
1760 bool final = false;
1761
1762
1763 LogFlowFunc(("entering, CmdSN=%d\n", pImage->CmdSN));
1764
1765 Assert(pRequest->enmXfer != SCSIXFER_TO_FROM_TARGET); /**< @todo not yet supported, would require AHS. */
1766 Assert(pRequest->cbI2TData <= 0xffffff); /* larger transfers would require R2T support. */
1767 Assert(pRequest->cbCDB <= 16); /* would cause buffer overrun below. */
1768
1769 /* If not in normal state, then the transport connection was dropped. Try
1770 * to reestablish by logging in, the target might be responsive again. */
1771 if (pImage->state == ISCSISTATE_FREE)
1772 rc = iscsiAttach(pImage);
1773
1774 /* If still not in normal state, then the underlying transport connection
1775 * cannot be established. Get out before bad things happen (and make
1776 * sure the caller suspends the VM again). */
1777 if (pImage->state != ISCSISTATE_NORMAL)
1778 {
1779 rc = VERR_NET_CONNECTION_REFUSED;
1780 goto out;
1781 }
1782
1783 /*
1784 * Send SCSI command to target with all I2T data included.
1785 */
1786 cbData = 0;
1787 if (pRequest->enmXfer == SCSIXFER_FROM_TARGET)
1788 cbData = (uint32_t)pRequest->cbT2IData;
1789 else
1790 cbData = (uint32_t)pRequest->cbI2TData;
1791
1792 RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
1793
1794 itt = iscsiNewITT(pImage);
1795 memset(aReqBHS, 0, sizeof(aReqBHS));
1796 aReqBHS[0] = RT_H2N_U32( ISCSI_FINAL_BIT | ISCSI_TASK_ATTR_ORDERED | ISCSIOP_SCSI_CMD
1797 | (pRequest->enmXfer << 21)); /* I=0,F=1,Attr=Ordered */
1798 aReqBHS[1] = RT_H2N_U32(0x00000000 | ((uint32_t)pRequest->cbI2TData & 0xffffff)); /* TotalAHSLength=0 */
1799 aReqBHS[2] = RT_H2N_U32(pImage->LUN >> 32);
1800 aReqBHS[3] = RT_H2N_U32(pImage->LUN & 0xffffffff);
1801 aReqBHS[4] = itt;
1802 aReqBHS[5] = RT_H2N_U32(cbData);
1803 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
1804 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
1805 memcpy(aReqBHS + 8, pRequest->pvCDB, pRequest->cbCDB);
1806 pImage->CmdSN++;
1807
1808 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
1809 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
1810 cnISCSIReq++;
1811
1812 if ( pRequest->enmXfer == SCSIXFER_TO_TARGET
1813 || pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET)
1814 {
1815 Assert(pRequest->cI2TSegs == 1);
1816 aISCSIReq[cnISCSIReq].pcvSeg = pRequest->paI2TSegs[0].pvSeg;
1817 aISCSIReq[cnISCSIReq].cbSeg = pRequest->paI2TSegs[0].cbSeg; /* Padding done by transport. */
1818 cnISCSIReq++;
1819 }
1820
1821 rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_DEFAULT);
1822 if (RT_FAILURE(rc))
1823 goto out_release;
1824
1825 /* Place SCSI request in queue. */
1826 pImage->paCurrReq = aISCSIReq;
1827 pImage->cnCurrReq = cnISCSIReq;
1828
1829 /*
1830 * Read SCSI response/data in PDUs from target.
1831 */
1832 if ( pRequest->enmXfer == SCSIXFER_FROM_TARGET
1833 || pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET)
1834 {
1835 Assert(pRequest->cT2ISegs == 1);
1836 pDst = (uint32_t *)pRequest->paT2ISegs[0].pvSeg;
1837 cbBufLength = pRequest->paT2ISegs[0].cbSeg;
1838 }
1839 else
1840 cbBufLength = 0;
1841
1842 do {
1843 uint32_t cnISCSIRes = 0;
1844 ISCSIRES aISCSIRes[4];
1845 uint32_t aResBHS[12];
1846
1847 aISCSIRes[cnISCSIRes].pvSeg = aResBHS;
1848 aISCSIRes[cnISCSIRes].cbSeg = sizeof(aResBHS);
1849 cnISCSIRes++;
1850 if (cbBufLength != 0 &&
1851 ( pRequest->enmXfer == SCSIXFER_FROM_TARGET
1852 || pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET))
1853 {
1854 aISCSIRes[cnISCSIRes].pvSeg = pDst;
1855 aISCSIRes[cnISCSIRes].cbSeg = cbBufLength;
1856 cnISCSIRes++;
1857 }
1858 /* Always reserve space for the status - it's impossible to tell
1859 * beforehand whether this will be the final PDU or not. */
1860 aISCSIRes[cnISCSIRes].pvSeg = aStatus;
1861 aISCSIRes[cnISCSIRes].cbSeg = sizeof(aStatus);
1862 cnISCSIRes++;
1863
1864 rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes);
1865 if (RT_FAILURE(rc))
1866 break;
1867
1868 final = !!(RT_N2H_U32(aResBHS[0]) & ISCSI_FINAL_BIT);
1869 ISCSIOPCODE cmd = (ISCSIOPCODE)(RT_N2H_U32(aResBHS[0]) & ISCSIOP_MASK);
1870 if (cmd == ISCSIOP_SCSI_RES)
1871 {
1872 /* This is the final PDU which delivers the status (and may be omitted if
1873 * the last Data-In PDU included successful completion status). Note
1874 * that ExpStatSN has been bumped already in iscsiRecvPDU. */
1875 if (!final || ((RT_N2H_U32(aResBHS[0]) & 0x0000ff00) != 0) || (RT_N2H_U32(aResBHS[6]) != pImage->ExpStatSN - 1))
1876 {
1877 /* SCSI Response in the wrong place or with a (target) failure. */
1878 rc = VERR_PARSE_ERROR;
1879 break;
1880 }
1881 /* The following is a bit tricky, as in error situations we may
1882 * get the status only instead of the result data plus optional
1883 * status. Thus the status may have ended up partially in the
1884 * data area. */
1885 pRequest->status = RT_N2H_U32(aResBHS[0]) & 0x000000ff;
1886 cbData = RT_N2H_U32(aResBHS[1]) & 0x00ffffff;
1887 if (cbData >= 2)
1888 {
1889 uint32_t cbStat = RT_N2H_U32(((uint32_t *)aISCSIRes[1].pvSeg)[0]) >> 16;
1890 if (cbStat + 2 > cbData)
1891 {
1892 rc = VERR_BUFFER_OVERFLOW;
1893 break;
1894 }
1895 /* Truncate sense data if it doesn't fit into the buffer. */
1896 pRequest->cbSense = RT_MIN(cbStat, pRequest->cbSense);
1897 memcpy(pRequest->pvSense,
1898 ((const char *)aISCSIRes[1].pvSeg) + 2,
1899 RT_MIN(aISCSIRes[1].cbSeg - 2, pRequest->cbSense));
1900 if ( cnISCSIRes > 2 && aISCSIRes[2].cbSeg
1901 && (ssize_t)pRequest->cbSense - aISCSIRes[1].cbSeg + 2 > 0)
1902 {
1903 memcpy((char *)pRequest->pvSense + aISCSIRes[1].cbSeg - 2,
1904 aISCSIRes[2].pvSeg,
1905 pRequest->cbSense - aISCSIRes[1].cbSeg + 2);
1906 }
1907 }
1908 else if (cbData == 1)
1909 {
1910 rc = VERR_PARSE_ERROR;
1911 break;
1912 }
1913 else
1914 pRequest->cbSense = 0;
1915 break;
1916 }
1917 else if (cmd == ISCSIOP_SCSI_DATA_IN)
1918 {
1919 /* A Data-In PDU carries some data that needs to be added to the received
1920 * data in response to the command. There may be both partial and complete
1921 * Data-In PDUs, so collect data until the status is included or the status
1922 * is sent in a separate SCSI Result frame (see above). */
1923 if (final && aISCSIRes[2].cbSeg != 0)
1924 {
1925 /* The received PDU is partially stored in the buffer for status.
1926 * Must not happen under normal circumstances and is a target error. */
1927 rc = VERR_BUFFER_OVERFLOW;
1928 break;
1929 }
1930 uint32_t len = RT_N2H_U32(aResBHS[1]) & 0x00ffffff;
1931 pDst = (uint32_t *)((char *)pDst + len);
1932 cbBufLength -= len;
1933 ExpDataSN++;
1934 if (final && (RT_N2H_U32(aResBHS[0]) & ISCSI_STATUS_BIT) != 0)
1935 {
1936 pRequest->status = RT_N2H_U32(aResBHS[0]) & 0x000000ff;
1937 pRequest->cbSense = 0;
1938 break;
1939 }
1940 }
1941 else
1942 {
1943 rc = VERR_PARSE_ERROR;
1944 break;
1945 }
1946 } while (true);
1947
1948 /* Remove SCSI request from queue. */
1949 pImage->paCurrReq = NULL;
1950 pImage->cnCurrReq = 0;
1951
1952out_release:
1953 if (rc == VERR_TIMEOUT)
1954 {
1955 /* Drop connection in case the target plays dead. Much better than
1956 * delaying the next requests until the timed out command actually
1957 * finishes. Also keep in mind that command shouldn't take longer than
1958 * about 30-40 seconds, or the guest will lose its patience. */
1959 iscsiTransportClose(pImage);
1960 pImage->state = ISCSISTATE_FREE;
1961 }
1962 RTSemMutexRelease(pImage->Mutex);
1963
1964out:
1965 LogFlowFunc(("returns %Rrc\n", rc));
1966 return rc;
1967}
1968
1969
1970/**
1971 * Generate a new Initiator Task Tag.
1972 *
1973 * @returns Initiator Task Tag.
1974 * @param pImage The iSCSI connection state to be used.
1975 */
1976static uint32_t iscsiNewITT(PISCSIIMAGE pImage)
1977{
1978 uint32_t next_itt;
1979
1980 next_itt = pImage->ITT++;
1981 if (pImage->ITT == ISCSI_TASK_TAG_RSVD)
1982 pImage->ITT = 0;
1983 return RT_H2N_U32(next_itt);
1984}
1985
1986
1987/**
1988 * Send an iSCSI request. The request can consist of several segments, which
1989 * are padded to 4 byte boundaries and concatenated.
1990 *
1991 * @returns VBOX status
1992 * @param pImage The iSCSI connection state to be used.
1993 * @param paReq Pointer to array of iSCSI request sections.
1994 * @param cnReq Number of valid iSCSI request sections in the array.
1995 * @param uFlags Flags controlling the exact send semantics.
1996 */
1997static int iscsiSendPDU(PISCSIIMAGE pImage, PISCSIREQ paReq, uint32_t cnReq,
1998 uint32_t uFlags)
1999{
2000 int rc = VINF_SUCCESS;
2001 /** @todo return VERR_VD_ISCSI_INVALID_STATE in the appropriate situations,
2002 * needs cleaning up of timeout/disconnect handling a bit, as otherwise
2003 * too many incorrect errors are signalled. */
2004 Assert(cnReq >= 1);
2005 Assert(paReq[0].cbSeg >= ISCSI_BHS_SIZE);
2006
2007 for (uint32_t i = 0; i < pImage->cISCSIRetries; i++)
2008 {
2009 rc = iscsiTransportWrite(pImage, paReq, cnReq);
2010 if (RT_SUCCESS(rc))
2011 break;
2012 if ( (uFlags & ISCSIPDU_NO_REATTACH)
2013 || (rc != VERR_BROKEN_PIPE && rc != VERR_NET_CONNECTION_REFUSED))
2014 break;
2015 /* No point in reestablishing the connection for a logout */
2016 if (pImage->state == ISCSISTATE_IN_LOGOUT)
2017 break;
2018 RTThreadSleep(500);
2019 if (pImage->state != ISCSISTATE_IN_LOGIN)
2020 {
2021 /* Attempt to re-login when a connection fails, but only when not
2022 * currently logging in. */
2023 rc = iscsiAttach(pImage);
2024 if (RT_FAILURE(rc))
2025 break;
2026 }
2027 }
2028 return rc;
2029}
2030
2031
2032/**
2033 * Wait for an iSCSI response with a matching Initiator Target Tag. The response is
2034 * split into several segments, as requested by the caller-provided buffer specification.
2035 * Remember that the response can be split into several PDUs by the sender, so make
2036 * sure that all parts are collected and processed appropriately by the caller.
2037 *
2038 * @returns VBOX status
2039 * @param pImage The iSCSI connection state to be used.
2040 * @param paRes Pointer to array of iSCSI response sections.
2041 * @param cnRes Number of valid iSCSI response sections in the array.
2042 */
2043static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes)
2044{
2045 int rc = VINF_SUCCESS;
2046 ISCSIRES aResBuf;
2047
2048 for (uint32_t i = 0; i < pImage->cISCSIRetries; i++)
2049 {
2050 aResBuf.pvSeg = pImage->pvRecvPDUBuf;
2051 aResBuf.cbSeg = pImage->cbRecvPDUBuf;
2052 rc = iscsiTransportRead(pImage, &aResBuf, 1);
2053 if (RT_FAILURE(rc))
2054 {
2055 if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
2056 {
2057 /* No point in reestablishing the connection for a logout */
2058 if (pImage->state == ISCSISTATE_IN_LOGOUT)
2059 break;
2060 /* Connection broken while waiting for a response - wait a while and
2061 * try to restart by re-sending the original request (if any).
2062 * This also handles the connection reestablishment (login etc.). */
2063 RTThreadSleep(500);
2064 if (pImage->state != ISCSISTATE_IN_LOGIN)
2065 {
2066 /* Attempt to re-login when a connection fails, but only when not
2067 * currently logging in. */
2068 rc = iscsiAttach(pImage);
2069 if (RT_FAILURE(rc))
2070 break;
2071 }
2072 if (pImage->paCurrReq != NULL)
2073 {
2074 rc = iscsiSendPDU(pImage, pImage->paCurrReq, pImage->cnCurrReq, ISCSIPDU_DEFAULT);
2075 if (RT_FAILURE(rc))
2076 break;
2077 }
2078 }
2079 else
2080 {
2081 /* Signal other errors (VERR_BUFFER_OVERFLOW etc.) to the caller. */
2082 break;
2083 }
2084 }
2085 else
2086 {
2087 ISCSIOPCODE cmd;
2088 const uint32_t *pcvResSeg = (const uint32_t *)aResBuf.pvSeg;
2089
2090 /* Check whether the received PDU is valid, and update the internal state of
2091 * the iSCSI connection/session. */
2092 rc = iscsiValidatePDU(&aResBuf, 1);
2093 if (RT_FAILURE(rc))
2094 continue;
2095 cmd = (ISCSIOPCODE)(RT_N2H_U32(pcvResSeg[0]) & ISCSIOP_MASK);
2096 switch (cmd)
2097 {
2098 case ISCSIOP_SCSI_RES:
2099 case ISCSIOP_SCSI_TASKMGMT_RES:
2100 case ISCSIOP_SCSI_DATA_IN:
2101 case ISCSIOP_R2T:
2102 case ISCSIOP_ASYN_MSG:
2103 case ISCSIOP_TEXT_RES:
2104 case ISCSIOP_LOGIN_RES:
2105 case ISCSIOP_LOGOUT_RES:
2106 case ISCSIOP_REJECT:
2107 case ISCSIOP_NOP_IN:
2108 if (serial_number_less(pImage->MaxCmdSN, RT_N2H_U32(pcvResSeg[8])))
2109 pImage->MaxCmdSN = RT_N2H_U32(pcvResSeg[8]);
2110 if (serial_number_less(pImage->ExpCmdSN, RT_N2H_U32(pcvResSeg[7])))
2111 pImage->ExpCmdSN = RT_N2H_U32(pcvResSeg[7]);
2112 break;
2113 default:
2114 rc = VERR_PARSE_ERROR;
2115 }
2116 if (RT_FAILURE(rc))
2117 continue;
2118 if ( !pImage->FirstRecvPDU
2119 && (cmd != ISCSIOP_SCSI_DATA_IN || (RT_N2H_U32(pcvResSeg[0]) & ISCSI_STATUS_BIT)))
2120 {
2121 if (pImage->ExpStatSN == RT_N2H_U32(pcvResSeg[6]))
2122 {
2123 /* StatSN counter is not advanced on R2T and on a target SN update NOP-In. */
2124 if ( (cmd != ISCSIOP_R2T)
2125 && ((cmd != ISCSIOP_NOP_IN) || (RT_N2H_U32(pcvResSeg[4]) != ISCSI_TASK_TAG_RSVD)))
2126 pImage->ExpStatSN++;
2127 }
2128 else
2129 {
2130 rc = VERR_PARSE_ERROR;
2131 continue;
2132 }
2133 }
2134 /* Finally check whether the received PDU matches what the caller wants. */
2135 if ( itt == pcvResSeg[4]
2136 && itt != ISCSI_TASK_TAG_RSVD)
2137 {
2138 /* Copy received PDU (one segment) to caller-provided buffers. */
2139 uint32_t j;
2140 size_t cbSeg;
2141 const uint8_t *pSrc;
2142
2143 pSrc = (const uint8_t *)aResBuf.pvSeg;
2144 cbSeg = aResBuf.cbSeg;
2145 for (j = 0; j < cnRes; j++)
2146 {
2147 if (cbSeg > paRes[j].cbSeg)
2148 {
2149 memcpy(paRes[j].pvSeg, pSrc, paRes[j].cbSeg);
2150 pSrc += paRes[j].cbSeg;
2151 cbSeg -= paRes[j].cbSeg;
2152 }
2153 else
2154 {
2155 memcpy(paRes[j].pvSeg, pSrc, cbSeg);
2156 paRes[j].cbSeg = cbSeg;
2157 cbSeg = 0;
2158 break;
2159 }
2160 }
2161 if (cbSeg != 0)
2162 {
2163 rc = VERR_BUFFER_OVERFLOW;
2164 break;
2165 }
2166 for (j++; j < cnRes; j++)
2167 paRes[j].cbSeg = 0;
2168 break;
2169 }
2170 else if ( cmd == ISCSIOP_NOP_IN
2171 && RT_N2H_U32(pcvResSeg[5]) != ISCSI_TASK_TAG_RSVD)
2172 {
2173 uint32_t cnISCSIReq;
2174 ISCSIREQ aISCSIReq[4];
2175 uint32_t aReqBHS[12];
2176
2177 aReqBHS[0] = RT_H2N_U32(ISCSI_IMMEDIATE_DELIVERY_BIT | ISCSI_FINAL_BIT | ISCSIOP_NOP_OUT);
2178 aReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
2179 aReqBHS[2] = pcvResSeg[2]; /* copy LUN from NOP-In */
2180 aReqBHS[3] = pcvResSeg[3]; /* copy LUN from NOP-In */
2181 aReqBHS[4] = RT_H2N_U32(ISCSI_TASK_TAG_RSVD); /* ITT, reply */
2182 aReqBHS[5] = pcvResSeg[5]; /* copy TTT from NOP-In */
2183 aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
2184 aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
2185 aReqBHS[8] = 0; /* reserved */
2186 aReqBHS[9] = 0; /* reserved */
2187 aReqBHS[10] = 0; /* reserved */
2188 aReqBHS[11] = 0; /* reserved */
2189
2190 cnISCSIReq = 0;
2191 aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
2192 aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
2193 cnISCSIReq++;
2194
2195 iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_NO_REATTACH);
2196 /* Break if the caller wanted to process the NOP-in only. */
2197 if (itt == ISCSI_TASK_TAG_RSVD)
2198 break;
2199 }
2200 }
2201 }
2202 return rc;
2203}
2204
2205
2206/**
2207 * Reset the PDU buffer
2208 *
2209 * @param pImage The iSCSI connection state to be used.
2210 */
2211static void iscsiRecvPDUReset(PISCSIIMAGE pImage)
2212{
2213 pImage->cbRecvPDUResidual = ISCSI_BHS_SIZE;
2214 pImage->fRecvPDUBHS = true;
2215 pImage->pbRecvPDUBufCur = (uint8_t *)pImage->pvRecvPDUBuf;
2216}
2217
2218static void iscsiPDUTxAdd(PISCSIIMAGE pImage, PISCSIPDUTX pIScsiPDUTx, bool fFront)
2219{
2220 if (!fFront)
2221 {
2222 /* Link the PDU at the tail of the list. */
2223 if (!pImage->pIScsiPDUTxHead)
2224 pImage->pIScsiPDUTxHead = pIScsiPDUTx;
2225 else
2226 pImage->pIScsiPDUTxTail->pNext = pIScsiPDUTx;
2227 pImage->pIScsiPDUTxTail = pIScsiPDUTx;
2228 }
2229 else
2230 {
2231 /* Link PDU to at the front of the list. */
2232 pIScsiPDUTx->pNext = pImage->pIScsiPDUTxHead;
2233 pImage->pIScsiPDUTxHead = pIScsiPDUTx;
2234 if (!pImage->pIScsiPDUTxTail)
2235 pImage->pIScsiPDUTxTail = pIScsiPDUTx;
2236 }
2237}
2238
2239/**
2240 * Receives a PDU in a non blocking way.
2241 *
2242 * @returns VBOX status code.
2243 * @param pImage The iSCSI connection state to be used.
2244 */
2245static int iscsiRecvPDUAsync(PISCSIIMAGE pImage)
2246{
2247 size_t cbActuallyRead = 0;
2248 int rc = VINF_SUCCESS;
2249
2250 LogFlowFunc(("pImage=%#p\n", pImage));
2251
2252 /* Check if we are in the middle of a PDU receive. */
2253 if (pImage->cbRecvPDUResidual == 0)
2254 {
2255 /*
2256 * We are receiving a new PDU, don't read more than the BHS initially
2257 * until we know the real size of the PDU.
2258 */
2259 iscsiRecvPDUReset(pImage);
2260 LogFlow(("Receiving new PDU\n"));
2261 }
2262
2263 rc = pImage->pInterfaceNetCallbacks->pfnReadNB(pImage->Socket, pImage->pbRecvPDUBufCur,
2264 pImage->cbRecvPDUResidual, &cbActuallyRead);
2265 if (RT_SUCCESS(rc) && cbActuallyRead == 0)
2266 rc = VERR_BROKEN_PIPE;
2267
2268 if (RT_SUCCESS(rc))
2269 {
2270 LogFlow(("Received %zu bytes\n", cbActuallyRead));
2271 pImage->cbRecvPDUResidual -= cbActuallyRead;
2272 pImage->pbRecvPDUBufCur += cbActuallyRead;
2273
2274 /* Check if we received everything we wanted. */
2275 if ( !pImage->cbRecvPDUResidual
2276 && pImage->fRecvPDUBHS)
2277 {
2278 size_t cbAHSLength, cbDataLength;
2279
2280 /* If we were reading the BHS first get the actual PDU size now. */
2281 uint32_t word1 = RT_N2H_U32(((uint32_t *)(pImage->pvRecvPDUBuf))[1]);
2282 cbAHSLength = (word1 & 0xff000000) >> 24;
2283 cbAHSLength = ((cbAHSLength - 1) | 3) + 1; /* Add padding. */
2284 cbDataLength = word1 & 0x00ffffff;
2285 cbDataLength = ((cbDataLength - 1) | 3) + 1; /* Add padding. */
2286 pImage->cbRecvPDUResidual = cbAHSLength + cbDataLength;
2287 pImage->fRecvPDUBHS = false; /* Start receiving the rest of the PDU. */
2288 }
2289
2290 if (!pImage->cbRecvPDUResidual)
2291 {
2292 /* We received the complete PDU with or without any payload now. */
2293 LogFlow(("Received complete PDU\n"));
2294 ISCSIRES aResBuf;
2295 aResBuf.pvSeg = pImage->pvRecvPDUBuf;
2296 aResBuf.cbSeg = pImage->cbRecvPDUBuf;
2297 rc = iscsiRecvPDUProcess(pImage, &aResBuf, 1);
2298 }
2299 }
2300 else
2301 LogFlowFunc(("Reading from the socket returned with rc=%Rrc\n", rc));
2302
2303 return rc;
2304}
2305
2306static int iscsiSendPDUAsync(PISCSIIMAGE pImage)
2307{
2308 size_t cbSent = 0;
2309 int rc = VINF_SUCCESS;
2310
2311 LogFlowFunc(("pImage=%#p\n", pImage));
2312
2313 do
2314 {
2315 /*
2316 * If there is no PDU active, get the first one from the list.
2317 * Check that we are allowed to transfer the PDU by comparing the
2318 * command sequence number and the maximum sequence number allowed by the target.
2319 */
2320 if (!pImage->pIScsiPDUTxCur)
2321 {
2322 if ( !pImage->pIScsiPDUTxHead
2323 || serial_number_greater(pImage->pIScsiPDUTxHead->CmdSN, pImage->MaxCmdSN))
2324 break;
2325
2326 pImage->pIScsiPDUTxCur = pImage->pIScsiPDUTxHead;
2327 pImage->pIScsiPDUTxHead = pImage->pIScsiPDUTxCur->pNext;
2328 if (!pImage->pIScsiPDUTxHead)
2329 pImage->pIScsiPDUTxTail = NULL;
2330 }
2331
2332 /* Send as much as we can. */
2333 rc = pImage->pInterfaceNetCallbacks->pfnSgWriteNB(pImage->Socket, &pImage->pIScsiPDUTxCur->SgBuf, &cbSent);
2334 LogFlow(("SgWriteNB returned rc=%Rrc cbSent=%zu\n", rc, cbSent));
2335 if (RT_SUCCESS(rc))
2336 {
2337 LogFlow(("Sent %zu bytes for PDU %#p\n", cbSent, pImage->pIScsiPDUTxCur));
2338 pImage->pIScsiPDUTxCur->cbSgLeft -= cbSent;
2339 RTSgBufAdvance(&pImage->pIScsiPDUTxCur->SgBuf, cbSent);
2340 if (!pImage->pIScsiPDUTxCur->cbSgLeft)
2341 {
2342 /* PDU completed, free it and place the command on the waiting for response list. */
2343 if (pImage->pIScsiPDUTxCur->pIScsiCmd)
2344 {
2345 LogFlow(("Sent complete PDU, placing on waiting list\n"));
2346 iscsiCmdInsert(pImage, pImage->pIScsiPDUTxCur->pIScsiCmd);
2347 }
2348 RTMemFree(pImage->pIScsiPDUTxCur);
2349 pImage->pIScsiPDUTxCur = NULL;
2350 }
2351 }
2352 } while ( RT_SUCCESS(rc)
2353 && !pImage->pIScsiPDUTxCur);
2354
2355 if (rc == VERR_TRY_AGAIN)
2356 rc = VINF_SUCCESS;
2357
2358 /* Add the write poll flag if we still have something to send, clear it otherwise. */
2359 if (pImage->pIScsiPDUTxCur)
2360 pImage->fPollEvents |= VD_INTERFACETCPNET_EVT_WRITE;
2361 else
2362 pImage->fPollEvents &= ~VD_INTERFACETCPNET_EVT_WRITE;
2363
2364 LogFlowFunc(("rc=%Rrc pIScsiPDUTxCur=%#p\n", rc, pImage->pIScsiPDUTxCur));
2365 return rc;
2366}
2367
2368/**
2369 * Process a received PDU.
2370 *
2371 * @return VBOX status code.
2372 * @param pImage The iSCSI connection state to be used.
2373 * @param paRes Pointer to the array of iSCSI repsonse sections.
2374 * @param cnRes Number of valid iSCSI response sections in the array.
2375 */
2376static int iscsiRecvPDUProcess(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes)
2377{
2378 int rc = VINF_SUCCESS;
2379
2380 LogFlowFunc(("pImage=%#p paRes=%#p cnRes=%u\n", pImage, paRes, cnRes));
2381
2382 /* Validate the PDU first. */
2383 rc = iscsiValidatePDU(paRes, cnRes);
2384 if (RT_SUCCESS(rc))
2385 {
2386 ISCSIOPCODE cmd;
2387 const uint32_t *pcvResSeg = (const uint32_t *)paRes[0].pvSeg;
2388
2389 Assert(paRes[0].cbSeg > 9 * sizeof(uint32_t));
2390
2391 do
2392 {
2393 cmd = (ISCSIOPCODE)(RT_N2H_U32(pcvResSeg[0]) & ISCSIOP_MASK);
2394 switch (cmd)
2395 {
2396 case ISCSIOP_SCSI_RES:
2397 case ISCSIOP_SCSI_TASKMGMT_RES:
2398 case ISCSIOP_SCSI_DATA_IN:
2399 case ISCSIOP_R2T:
2400 case ISCSIOP_ASYN_MSG:
2401 case ISCSIOP_TEXT_RES:
2402 case ISCSIOP_LOGIN_RES:
2403 case ISCSIOP_LOGOUT_RES:
2404 case ISCSIOP_REJECT:
2405 case ISCSIOP_NOP_IN:
2406 if (serial_number_less(pImage->MaxCmdSN, RT_N2H_U32(pcvResSeg[8])))
2407 pImage->MaxCmdSN = RT_N2H_U32(pcvResSeg[8]);
2408 if (serial_number_less(pImage->ExpCmdSN, RT_N2H_U32(pcvResSeg[7])))
2409 pImage->ExpCmdSN = RT_N2H_U32(pcvResSeg[7]);
2410 break;
2411 default:
2412 rc = VERR_PARSE_ERROR;
2413 }
2414
2415 if (RT_FAILURE(rc))
2416 break;
2417
2418 if ( !pImage->FirstRecvPDU
2419 && (cmd != ISCSIOP_SCSI_DATA_IN || (RT_N2H_U32(pcvResSeg[0]) & ISCSI_STATUS_BIT)))
2420 {
2421 if (pImage->ExpStatSN == RT_N2H_U32(pcvResSeg[6]))
2422 {
2423 /* StatSN counter is not advanced on R2T and on a target SN update NOP-In. */
2424 if ( (cmd != ISCSIOP_R2T)
2425 && ((cmd != ISCSIOP_NOP_IN) || (RT_N2H_U32(pcvResSeg[4]) != ISCSI_TASK_TAG_RSVD)))
2426 pImage->ExpStatSN++;
2427 }
2428 else
2429 {
2430 rc = VERR_PARSE_ERROR;
2431 break;
2432 }
2433 }
2434
2435 if (pcvResSeg[4] != ISCSI_TASK_TAG_RSVD)
2436 {
2437 /*
2438 * This is a response from the target for a request from the initiator.
2439 * Get the request and update its state.
2440 */
2441 rc = iscsiRecvPDUUpdateRequest(pImage, paRes, cnRes);
2442 /* Try to send more PDUs now that we updated the MaxCmdSN field */
2443 if ( RT_SUCCESS(rc)
2444 && !pImage->pIScsiPDUTxCur)
2445 rc = iscsiSendPDUAsync(pImage);
2446 }
2447 else
2448 {
2449 /* This is a target initiated request (we handle only NOP-In request at the moment). */
2450 if ( cmd == ISCSIOP_NOP_IN
2451 && RT_N2H_U32(pcvResSeg[5]) != ISCSI_TASK_TAG_RSVD)
2452 {
2453 PISCSIPDUTX pIScsiPDUTx;
2454 uint32_t cnISCSIReq;
2455 uint32_t *paReqBHS;
2456
2457 LogFlowFunc(("Sending NOP-Out\n"));
2458
2459 /* Allocate a new PDU initialize it and put onto the waiting list. */
2460 pIScsiPDUTx = (PISCSIPDUTX)RTMemAllocZ(sizeof(ISCSIPDUTX));
2461 if (!pIScsiPDUTx)
2462 {
2463 rc = VERR_NO_MEMORY;
2464 break;
2465 }
2466 paReqBHS = &pIScsiPDUTx->aBHS[0];
2467 paReqBHS[0] = RT_H2N_U32(ISCSI_IMMEDIATE_DELIVERY_BIT | ISCSI_FINAL_BIT | ISCSIOP_NOP_OUT);
2468 paReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
2469 paReqBHS[2] = pcvResSeg[2]; /* copy LUN from NOP-In */
2470 paReqBHS[3] = pcvResSeg[3]; /* copy LUN from NOP-In */
2471 paReqBHS[4] = RT_H2N_U32(ISCSI_TASK_TAG_RSVD); /* ITT, reply */
2472 paReqBHS[5] = pcvResSeg[5]; /* copy TTT from NOP-In */
2473 paReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
2474 paReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
2475 paReqBHS[8] = 0; /* reserved */
2476 paReqBHS[9] = 0; /* reserved */
2477 paReqBHS[10] = 0; /* reserved */
2478 paReqBHS[11] = 0; /* reserved */
2479
2480 cnISCSIReq = 0;
2481 pIScsiPDUTx->aISCSIReq[cnISCSIReq].pvSeg = paReqBHS;
2482 pIScsiPDUTx->aISCSIReq[cnISCSIReq].cbSeg = sizeof(pIScsiPDUTx->aBHS);
2483 cnISCSIReq++;
2484 pIScsiPDUTx->cbSgLeft = sizeof(pIScsiPDUTx->aBHS);
2485 RTSgBufInit(&pIScsiPDUTx->SgBuf, pIScsiPDUTx->aISCSIReq, cnISCSIReq);
2486
2487 /* Link the PDU to the list. */
2488 iscsiPDUTxAdd(pImage, pIScsiPDUTx, false /* fFront */);
2489
2490 /* Start transfer of a PDU if there is no one active at the moment. */
2491 if (!pImage->pIScsiPDUTxCur)
2492 rc = iscsiSendPDUAsync(pImage);
2493 }
2494 }
2495 } while (0);
2496 }
2497
2498 return rc;
2499}
2500
2501/**
2502 * Check the static (not dependent on the connection/session state) validity of an iSCSI response PDU.
2503 *
2504 * @returns VBOX status
2505 * @param paRes Pointer to array of iSCSI response sections.
2506 * @param cnRes Number of valid iSCSI response sections in the array.
2507 */
2508static int iscsiValidatePDU(PISCSIRES paRes, uint32_t cnRes)
2509{
2510 const uint32_t *pcrgResBHS;
2511 uint32_t hw0;
2512 Assert(cnRes >= 1);
2513 Assert(paRes[0].cbSeg >= ISCSI_BHS_SIZE);
2514
2515 LogFlowFunc(("paRes=%#p cnRes=%u\n", paRes, cnRes));
2516
2517 pcrgResBHS = (const uint32_t *)(paRes[0].pvSeg);
2518 hw0 = RT_N2H_U32(pcrgResBHS[0]);
2519 switch (hw0 & ISCSIOP_MASK)
2520 {
2521 case ISCSIOP_NOP_IN:
2522 /* NOP-In responses must not be split into several PDUs nor it may contain
2523 * ping data for target-initiated pings nor may both task tags be valid task tags. */
2524 if ( (hw0 & ISCSI_FINAL_BIT) == 0
2525 || ( RT_N2H_U32(pcrgResBHS[4]) == ISCSI_TASK_TAG_RSVD
2526 && RT_N2H_U32(pcrgResBHS[1]) != 0)
2527 || ( RT_N2H_U32(pcrgResBHS[4]) != ISCSI_TASK_TAG_RSVD
2528 && RT_N2H_U32(pcrgResBHS[5]) != ISCSI_TASK_TAG_RSVD))
2529 return VERR_PARSE_ERROR;
2530 break;
2531 case ISCSIOP_SCSI_RES:
2532 /* SCSI responses must not be split into several PDUs nor must the residual
2533 * bits be contradicting each other nor may the residual bits be set for PDUs
2534 * containing anything else but a completed command response. Underflow
2535 * is no reason for declaring a PDU as invalid, as the target may choose
2536 * to return less data than we assume to get. */
2537 if ( (hw0 & ISCSI_FINAL_BIT) == 0
2538 || ((hw0 & ISCSI_BI_READ_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_BI_READ_RESIDUAL_UNFL_BIT))
2539 || ((hw0 & ISCSI_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_RESIDUAL_UNFL_BIT))
2540 || ( ((hw0 & ISCSI_SCSI_RESPONSE_MASK) == 0)
2541 && ((hw0 & ISCSI_SCSI_STATUS_MASK) == SCSI_STATUS_OK)
2542 && (hw0 & ( ISCSI_BI_READ_RESIDUAL_OVFL_BIT | ISCSI_BI_READ_RESIDUAL_UNFL_BIT
2543 | ISCSI_RESIDUAL_OVFL_BIT))))
2544 return VERR_PARSE_ERROR;
2545 break;
2546 case ISCSIOP_LOGIN_RES:
2547 /* Login responses must not contain contradicting transit and continue bits. */
2548 if ((hw0 & ISCSI_CONTINUE_BIT) && ((hw0 & ISCSI_TRANSIT_BIT) != 0))
2549 return VERR_PARSE_ERROR;
2550 break;
2551 case ISCSIOP_TEXT_RES:
2552 /* Text responses must not contain contradicting final and continue bits nor
2553 * may the final bit be set for PDUs containing a target transfer tag other than
2554 * the reserved transfer tag (and vice versa). */
2555 if ( (((hw0 & ISCSI_CONTINUE_BIT) && (hw0 & ISCSI_FINAL_BIT) != 0))
2556 || (((hw0 & ISCSI_FINAL_BIT) && (RT_N2H_U32(pcrgResBHS[5]) != ISCSI_TASK_TAG_RSVD)))
2557 || (((hw0 & ISCSI_FINAL_BIT) == 0) && (RT_N2H_U32(pcrgResBHS[5]) == ISCSI_TASK_TAG_RSVD)))
2558 return VERR_PARSE_ERROR;
2559 break;
2560 case ISCSIOP_SCSI_DATA_IN:
2561 /* SCSI Data-in responses must not contain contradicting residual bits when
2562 * status bit is set. */
2563 if ((hw0 & ISCSI_STATUS_BIT) && (hw0 & ISCSI_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_RESIDUAL_UNFL_BIT))
2564 return VERR_PARSE_ERROR;
2565 break;
2566 case ISCSIOP_LOGOUT_RES:
2567 /* Logout responses must not have the final bit unset and may not contain any
2568 * data or additional header segments. */
2569 if ( ((hw0 & ISCSI_FINAL_BIT) == 0)
2570 || (RT_N2H_U32(pcrgResBHS[1]) != 0))
2571 return VERR_PARSE_ERROR;
2572 break;
2573 case ISCSIOP_ASYN_MSG:
2574 /* Asynchronous Messages must not have the final bit unset and may not contain
2575 * an initiator task tag. */
2576 if ( ((hw0 & ISCSI_FINAL_BIT) == 0)
2577 || (RT_N2H_U32(pcrgResBHS[4]) != ISCSI_TASK_TAG_RSVD))
2578 return VERR_PARSE_ERROR;
2579 break;
2580 case ISCSIOP_SCSI_TASKMGMT_RES:
2581 case ISCSIOP_R2T:
2582 case ISCSIOP_REJECT:
2583 default:
2584 /* Do some logging, ignore PDU. */
2585 LogFlowFunc(("ignore unhandled PDU, first word %#08x\n", RT_N2H_U32(pcrgResBHS[0])));
2586 return VERR_PARSE_ERROR;
2587 }
2588 /* A target must not send PDUs with MaxCmdSN less than ExpCmdSN-1. */
2589
2590 if (serial_number_less(RT_N2H_U32(pcrgResBHS[8]), RT_N2H_U32(pcrgResBHS[7])-1))
2591 return VERR_PARSE_ERROR;
2592
2593 return VINF_SUCCESS;
2594}
2595
2596
2597/**
2598 * Prepares a PDU to transfer for the given command and adds it to the list.
2599 */
2600static int iscsiPDUTxPrepare(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd)
2601{
2602 int rc = VINF_SUCCESS;
2603 uint32_t *paReqBHS;
2604 size_t cbData = 0;
2605 size_t cbSegs = 0;
2606 PSCSIREQ pScsiReq;
2607 PISCSIPDUTX pIScsiPDU = NULL;
2608
2609 LogFlowFunc(("pImage=%#p pIScsiCmd=%#p\n", pImage, pIScsiCmd));
2610
2611 Assert(pIScsiCmd->enmCmdType == ISCSICMDTYPE_REQ);
2612
2613 pIScsiCmd->Itt = iscsiNewITT(pImage);
2614 pScsiReq = pIScsiCmd->CmdType.ScsiReq.pScsiReq;
2615
2616 if (pScsiReq->cT2ISegs)
2617 RTSgBufInit(&pScsiReq->SgBufT2I, pScsiReq->paT2ISegs, pScsiReq->cT2ISegs);
2618
2619 /*
2620 * Allocate twice as much entries as required for padding (worst case).
2621 * The additional segment is for the BHS.
2622 */
2623 size_t cI2TSegs = 2*(pScsiReq->cI2TSegs + 1);
2624 pIScsiPDU = (PISCSIPDUTX)RTMemAllocZ(RT_OFFSETOF(ISCSIPDUTX, aISCSIReq[cI2TSegs]));
2625 if (!pIScsiPDU)
2626 return VERR_NO_MEMORY;
2627
2628 pIScsiPDU->pIScsiCmd = pIScsiCmd;
2629
2630 if (pScsiReq->enmXfer == SCSIXFER_FROM_TARGET)
2631 cbData = (uint32_t)pScsiReq->cbT2IData;
2632 else
2633 cbData = (uint32_t)pScsiReq->cbI2TData;
2634
2635 paReqBHS = pIScsiPDU->aBHS;
2636
2637 /* Setup the BHS. */
2638 paReqBHS[0] = RT_H2N_U32( ISCSI_FINAL_BIT | ISCSI_TASK_ATTR_ORDERED | ISCSIOP_SCSI_CMD
2639 | (pScsiReq->enmXfer << 21)); /* I=0,F=1,Attr=Ordered */
2640 paReqBHS[1] = RT_H2N_U32(0x00000000 | ((uint32_t)pScsiReq->cbI2TData & 0xffffff)); /* TotalAHSLength=0 */
2641 paReqBHS[2] = RT_H2N_U32(pImage->LUN >> 32);
2642 paReqBHS[3] = RT_H2N_U32(pImage->LUN & 0xffffffff);
2643 paReqBHS[4] = pIScsiCmd->Itt;
2644 paReqBHS[5] = RT_H2N_U32(cbData);
2645 paReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
2646 paReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
2647 memcpy(paReqBHS + 8, pScsiReq->pvCDB, pScsiReq->cbCDB);
2648
2649 pIScsiPDU->CmdSN = pImage->CmdSN;
2650 pImage->CmdSN++;
2651
2652 /* Setup the S/G buffers. */
2653 uint32_t cnISCSIReq = 0;
2654 pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = sizeof(pIScsiPDU->aBHS);
2655 pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = pIScsiPDU->aBHS;
2656 cnISCSIReq++;
2657 cbSegs = sizeof(pIScsiPDU->aBHS);
2658 /* Padding is not necessary for the BHS. */
2659
2660 if (pScsiReq->cbI2TData)
2661 {
2662 for (unsigned cSeg = 0; cSeg < pScsiReq->cI2TSegs; cSeg++)
2663 {
2664 Assert(cnISCSIReq < cI2TSegs);
2665 pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = pScsiReq->paI2TSegs[cSeg].cbSeg;
2666 pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = pScsiReq->paI2TSegs[cSeg].pvSeg;
2667 cbSegs += pScsiReq->paI2TSegs[cSeg].cbSeg;
2668 cnISCSIReq++;
2669
2670 /* Add padding if necessary. */
2671 if (pScsiReq->paI2TSegs[cSeg].cbSeg & 3)
2672 {
2673 Assert(cnISCSIReq < cI2TSegs);
2674 pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = &pImage->aPadding[0];
2675 pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = 4 - (pScsiReq->paI2TSegs[cSeg].cbSeg & 3);
2676 cbSegs += pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg;
2677 cnISCSIReq++;
2678 }
2679 }
2680 }
2681
2682 pIScsiPDU->cISCSIReq = cnISCSIReq;
2683 pIScsiPDU->cbSgLeft = cbSegs;
2684 RTSgBufInit(&pIScsiPDU->SgBuf, pIScsiPDU->aISCSIReq, cnISCSIReq);
2685
2686 /* Link the PDU to the list. */
2687 iscsiPDUTxAdd(pImage, pIScsiPDU, false /* fFront */);
2688
2689 /* Start transfer of a PDU if there is no one active at the moment. */
2690 if (!pImage->pIScsiPDUTxCur)
2691 rc = iscsiSendPDUAsync(pImage);
2692
2693 return rc;
2694}
2695
2696
2697/**
2698 * Updates the state of a request from the PDU we received.
2699 *
2700 * @return VBox status code.
2701 * @param pImage iSCSI connection state to use.
2702 * @param paRes Pointer to array of iSCSI response sections.
2703 * @param cnRes Number of valid iSCSI response sections in the array.
2704 */
2705static int iscsiRecvPDUUpdateRequest(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes)
2706{
2707 int rc = VINF_SUCCESS;
2708 PISCSICMD pIScsiCmd;
2709 uint32_t *paResBHS;
2710
2711 LogFlowFunc(("pImage=%#p paRes=%#p cnRes=%u\n", pImage, paRes, cnRes));
2712
2713 Assert(cnRes == 1);
2714 Assert(paRes[0].cbSeg >= ISCSI_BHS_SIZE);
2715
2716 paResBHS = (uint32_t *)paRes[0].pvSeg;
2717
2718 pIScsiCmd = iscsiCmdGetFromItt(pImage, paResBHS[4]);
2719
2720 if (pIScsiCmd)
2721 {
2722 bool final = false;
2723 PSCSIREQ pScsiReq;
2724
2725 LogFlow(("Found SCSI command %#p for Itt=%#u\n", pIScsiCmd, paResBHS[4]));
2726
2727 Assert(pIScsiCmd->enmCmdType == ISCSICMDTYPE_REQ);
2728 pScsiReq = pIScsiCmd->CmdType.ScsiReq.pScsiReq;
2729
2730 final = !!(RT_N2H_U32(paResBHS[0]) & ISCSI_FINAL_BIT);
2731 ISCSIOPCODE cmd = (ISCSIOPCODE)(RT_N2H_U32(paResBHS[0]) & ISCSIOP_MASK);
2732 if (cmd == ISCSIOP_SCSI_RES)
2733 {
2734 /* This is the final PDU which delivers the status (and may be omitted if
2735 * the last Data-In PDU included successful completion status). Note
2736 * that ExpStatSN has been bumped already in iscsiRecvPDU. */
2737 if (!final || ((RT_N2H_U32(paResBHS[0]) & 0x0000ff00) != 0) || (RT_N2H_U32(paResBHS[6]) != pImage->ExpStatSN - 1))
2738 {
2739 /* SCSI Response in the wrong place or with a (target) failure. */
2740 LogFlow(("Wrong ExpStatSN value in PDU\n"));
2741 rc = VERR_PARSE_ERROR;
2742 }
2743 else
2744 {
2745 pScsiReq->status = RT_N2H_U32(paResBHS[0]) & 0x000000ff;
2746 size_t cbData = RT_N2H_U32(paResBHS[1]) & 0x00ffffff;
2747 void *pvSense = (uint8_t *)paRes[0].pvSeg + ISCSI_BHS_SIZE;
2748
2749 if (cbData >= 2)
2750 {
2751 uint32_t cbStat = RT_N2H_U32(((uint32_t *)pvSense)[0]) >> 16;
2752 if (cbStat + 2 > cbData)
2753 {
2754 rc = VERR_BUFFER_OVERFLOW;
2755 }
2756 else
2757 {
2758 /* Truncate sense data if it doesn't fit into the buffer. */
2759 pScsiReq->cbSense = RT_MIN(cbStat, pScsiReq->cbSense);
2760 memcpy(pScsiReq->pvSense, (uint8_t *)pvSense + 2,
2761 RT_MIN(paRes[0].cbSeg - ISCSI_BHS_SIZE - 2, pScsiReq->cbSense));
2762 }
2763 }
2764 else if (cbData == 1)
2765 rc = VERR_PARSE_ERROR;
2766 else
2767 pScsiReq->cbSense = 0;
2768 }
2769 iscsiCmdComplete(pImage, pIScsiCmd, rc);
2770 }
2771 else if (cmd == ISCSIOP_SCSI_DATA_IN)
2772 {
2773 /* A Data-In PDU carries some data that needs to be added to the received
2774 * data in response to the command. There may be both partial and complete
2775 * Data-In PDUs, so collect data until the status is included or the status
2776 * is sent in a separate SCSI Result frame (see above). */
2777 size_t cbData = RT_N2H_U32(paResBHS[1]) & 0x00ffffff;
2778 void *pvData = (uint8_t *)paRes[0].pvSeg + ISCSI_BHS_SIZE;
2779
2780 if (final && cbData > pScsiReq->cbT2IData)
2781 {
2782 /* The received PDU is bigger than what we requested.
2783 * Must not happen under normal circumstances and is a target error. */
2784 rc = VERR_BUFFER_OVERFLOW;
2785 }
2786 else
2787 {
2788 /* Copy data from the received PDU into the T2I segments. */
2789 size_t cbCopied = RTSgBufCopyFromBuf(&pScsiReq->SgBufT2I, pvData, cbData);
2790 Assert(cbCopied == cbData);
2791
2792 if (final && (RT_N2H_U32(paResBHS[0]) & ISCSI_STATUS_BIT) != 0)
2793 {
2794 pScsiReq->status = RT_N2H_U32(paResBHS[0]) & 0x000000ff;
2795 pScsiReq->cbSense = 0;
2796 iscsiCmdComplete(pImage, pIScsiCmd, VINF_SUCCESS);
2797 }
2798 }
2799 }
2800 else
2801 rc = VERR_PARSE_ERROR;
2802 }
2803
2804 /* Log any errors here but ignore the PDU. */
2805 if (RT_FAILURE(rc))
2806 {
2807 LogRel(("iSCSI: Received malformed PDU from target %s (rc=%Rrc), ignoring\n", pImage->pszTargetName, rc));
2808 rc = VINF_SUCCESS;
2809 }
2810
2811 return rc;
2812}
2813
2814/**
2815 * Appends a key-value pair to the buffer. Normal ASCII strings (cbValue == 0) and large binary values
2816 * of a given length (cbValue > 0) are directly supported. Other value types must be converted to ASCII
2817 * by the caller. Strings must be in UTF-8 encoding.
2818 *
2819 * @returns VBOX status
2820 * @param pbBuf Pointer to the key-value buffer.
2821 * @param cbBuf Length of the key-value buffer.
2822 * @param pcbBufCurr Currently used portion of the key-value buffer.
2823 * @param pszKey Pointer to a string containing the key.
2824 * @param pszValue Pointer to either a string containing the value or to a large binary value.
2825 * @param cbValue Length of the binary value if applicable.
2826 */
2827static int iscsiTextAddKeyValue(uint8_t *pbBuf, size_t cbBuf, size_t *pcbBufCurr, const char *pcszKey,
2828 const char *pcszValue, size_t cbValue)
2829{
2830 size_t cbBufTmp = *pcbBufCurr;
2831 size_t cbKey = strlen(pcszKey);
2832 size_t cbValueEnc;
2833 uint8_t *pbCurr;
2834
2835 if (cbValue == 0)
2836 cbValueEnc = strlen(pcszValue);
2837 else
2838 cbValueEnc = cbValue * 2 + 2; /* 2 hex bytes per byte, 2 bytes prefix */
2839
2840 if (cbBuf < cbBufTmp + cbKey + 1 + cbValueEnc + 1)
2841 {
2842 /* Buffer would overflow, signal error. */
2843 return VERR_BUFFER_OVERFLOW;
2844 }
2845
2846 /*
2847 * Append a key=value pair (zero terminated string) to the end of the buffer.
2848 */
2849 pbCurr = pbBuf + cbBufTmp;
2850 memcpy(pbCurr, pcszKey, cbKey);
2851 pbCurr += cbKey;
2852 *pbCurr++ = '=';
2853 if (cbValue == 0)
2854 {
2855 memcpy(pbCurr, pcszValue, cbValueEnc);
2856 pbCurr += cbValueEnc;
2857 }
2858 else
2859 {
2860 *pbCurr++ = '0';
2861 *pbCurr++ = 'x';
2862 for (uint32_t i = 0; i < cbValue; i++)
2863 {
2864 uint8_t b;
2865 b = pcszValue[i];
2866 *pbCurr++ = NUM_2_HEX(b >> 4);
2867 *pbCurr++ = NUM_2_HEX(b & 0xf);
2868 }
2869 }
2870 *pbCurr = '\0';
2871 *pcbBufCurr = cbBufTmp + cbKey + 1 + cbValueEnc + 1;
2872
2873 return VINF_SUCCESS;
2874}
2875
2876
2877/**
2878 * Retrieve the value for a given key from the key=value buffer.
2879 *
2880 * @returns VBOX status.
2881 * @param pbBuf Buffer containing key=value pairs.
2882 * @param cbBuf Length of buffer with key=value pairs.
2883 * @param pszKey Pointer to key for which to retrieve the value.
2884 * @param ppszValue Pointer to value string pointer.
2885 */
2886static int iscsiTextGetKeyValue(const uint8_t *pbBuf, size_t cbBuf, const char *pcszKey, const char **ppcszValue)
2887{
2888 size_t cbKey = strlen(pcszKey);
2889
2890 while (cbBuf != 0)
2891 {
2892 size_t cbKeyValNull = strlen((const char *)pbBuf) + 1;
2893
2894 if (strncmp(pcszKey, (const char *)pbBuf, cbKey) == 0 && pbBuf[cbKey] == '=')
2895 {
2896 *ppcszValue = (const char *)(pbBuf + cbKey + 1);
2897 return VINF_SUCCESS;
2898 }
2899 pbBuf += cbKeyValNull;
2900 cbBuf -= cbKeyValNull;
2901 }
2902 return VERR_INVALID_NAME;
2903}
2904
2905
2906/**
2907 * Convert a long-binary value from a value string to the binary representation.
2908 *
2909 * @returns VBOX status
2910 * @param pszValue Pointer to a string containing the textual value representation.
2911 * @param pbValue Pointer to the value buffer for the binary value.
2912 * @param pcbValue In: length of value buffer, out: actual length of binary value.
2913 */
2914static int iscsiStrToBinary(const char *pcszValue, uint8_t *pbValue, size_t *pcbValue)
2915{
2916 size_t cbValue = *pcbValue;
2917 char c1, c2, c3, c4;
2918 Assert(cbValue >= 1);
2919
2920 if (strlen(pcszValue) < 3)
2921 return VERR_PARSE_ERROR;
2922 if (*pcszValue++ != '0')
2923 return VERR_PARSE_ERROR;
2924 switch (*pcszValue++)
2925 {
2926 case 'x':
2927 case 'X':
2928 if (strlen(pcszValue) & 1)
2929 {
2930 c1 = *pcszValue++;
2931 *pbValue++ = HEX_2_NUM(c1);
2932 cbValue--;
2933 }
2934 while (*pcszValue != '\0')
2935 {
2936 if (cbValue == 0)
2937 return VERR_BUFFER_OVERFLOW;
2938 c1 = *pcszValue++;
2939 if ((c1 < '0' || c1 > '9') && (c1 < 'a' || c1 > 'f') && (c1 < 'A' || c1 > 'F'))
2940 return VERR_PARSE_ERROR;
2941 c2 = *pcszValue++;
2942 if ((c2 < '0' || c2 > '9') && (c2 < 'a' || c2 > 'f') && (c2 < 'A' || c2 > 'F'))
2943 return VERR_PARSE_ERROR;
2944 *pbValue++ = (HEX_2_NUM(c1) << 4) | HEX_2_NUM(c2);
2945 cbValue--;
2946 }
2947 *pcbValue -= cbValue;
2948 break;
2949 case 'b':
2950 case 'B':
2951 if ((strlen(pcszValue) & 3) != 0)
2952 return VERR_PARSE_ERROR;
2953 while (*pcszValue != '\0')
2954 {
2955 uint32_t temp;
2956 if (cbValue == 0)
2957 return VERR_BUFFER_OVERFLOW;
2958 c1 = *pcszValue++;
2959 if ((c1 < 'A' || c1 > 'Z') && (c1 < 'a' || c1 >'z') && (c1 < '0' || c1 > '9') && (c1 != '+') && (c1 != '/'))
2960 return VERR_PARSE_ERROR;
2961 c2 = *pcszValue++;
2962 if ((c2 < 'A' || c2 > 'Z') && (c2 < 'a' || c2 >'z') && (c2 < '0' || c2 > '9') && (c2 != '+') && (c2 != '/'))
2963 return VERR_PARSE_ERROR;
2964 c3 = *pcszValue++;
2965 if ((c3 < 'A' || c3 > 'Z') && (c3 < 'a' || c3 >'z') && (c3 < '0' || c3 > '9') && (c3 != '+') && (c3 != '/') && (c3 != '='))
2966 return VERR_PARSE_ERROR;
2967 c4 = *pcszValue++;
2968 if ( (c3 == '=' && c4 != '=')
2969 || ((c4 < 'A' || c4 > 'Z') && (c4 < 'a' || c4 >'z') && (c4 < '0' || c4 > '9') && (c4 != '+') && (c4 != '/') && (c4 != '=')))
2970 return VERR_PARSE_ERROR;
2971 temp = (B64_2_NUM(c1) << 18) | (B64_2_NUM(c2) << 12);
2972 if (c3 == '=') {
2973 if (*pcszValue != '\0')
2974 return VERR_PARSE_ERROR;
2975 *pbValue++ = temp >> 16;
2976 cbValue--;
2977 } else {
2978 temp |= B64_2_NUM(c3) << 6;
2979 if (c4 == '=') {
2980 if (*pcszValue != '\0')
2981 return VERR_PARSE_ERROR;
2982 if (cbValue < 2)
2983 return VERR_BUFFER_OVERFLOW;
2984 *pbValue++ = temp >> 16;
2985 *pbValue++ = (temp >> 8) & 0xff;
2986 cbValue -= 2;
2987 }
2988 else
2989 {
2990 temp |= B64_2_NUM(c4);
2991 if (cbValue < 3)
2992 return VERR_BUFFER_OVERFLOW;
2993 *pbValue++ = temp >> 16;
2994 *pbValue++ = (temp >> 8) & 0xff;
2995 *pbValue++ = temp & 0xff;
2996 cbValue -= 3;
2997 }
2998 }
2999 }
3000 *pcbValue -= cbValue;
3001 break;
3002 default:
3003 return VERR_PARSE_ERROR;
3004 }
3005 return VINF_SUCCESS;
3006}
3007
3008
3009/**
3010 * Retrieve the relevant parameter values and update the initiator state.
3011 *
3012 * @returns VBOX status.
3013 * @param pImage Current iSCSI initiator state.
3014 * @param pbBuf Buffer containing key=value pairs.
3015 * @param cbBuf Length of buffer with key=value pairs.
3016 */
3017static int iscsiUpdateParameters(PISCSIIMAGE pImage, const uint8_t *pbBuf, size_t cbBuf)
3018{
3019 int rc;
3020 const char *pcszMaxRecvDataSegmentLength = NULL;
3021 const char *pcszMaxBurstLength = NULL;
3022 const char *pcszFirstBurstLength = NULL;
3023 rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "MaxRecvDataSegmentLength", &pcszMaxRecvDataSegmentLength);
3024 if (rc == VERR_INVALID_NAME)
3025 rc = VINF_SUCCESS;
3026 if (RT_FAILURE(rc))
3027 return VERR_PARSE_ERROR;
3028 rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "MaxBurstLength", &pcszMaxBurstLength);
3029 if (rc == VERR_INVALID_NAME)
3030 rc = VINF_SUCCESS;
3031 if (RT_FAILURE(rc))
3032 return VERR_PARSE_ERROR;
3033 rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "FirstBurstLength", &pcszFirstBurstLength);
3034 if (rc == VERR_INVALID_NAME)
3035 rc = VINF_SUCCESS;
3036 if (RT_FAILURE(rc))
3037 return VERR_PARSE_ERROR;
3038 if (pcszMaxRecvDataSegmentLength)
3039 {
3040 uint32_t cb = pImage->cbSendDataLength;
3041 rc = RTStrToUInt32Full(pcszMaxRecvDataSegmentLength, 0, &cb);
3042 AssertRC(rc);
3043 pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
3044 }
3045 if (pcszMaxBurstLength)
3046 {
3047 uint32_t cb = pImage->cbSendDataLength;
3048 rc = RTStrToUInt32Full(pcszMaxBurstLength, 0, &cb);
3049 AssertRC(rc);
3050 pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
3051 }
3052 if (pcszFirstBurstLength)
3053 {
3054 uint32_t cb = pImage->cbSendDataLength;
3055 rc = RTStrToUInt32Full(pcszFirstBurstLength, 0, &cb);
3056 AssertRC(rc);
3057 pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
3058 }
3059 return VINF_SUCCESS;
3060}
3061
3062
3063static bool serial_number_less(uint32_t s1, uint32_t s2)
3064{
3065 return (s1 < s2 && s2 - s1 < 0x80000000) || (s1 > s2 && s1 - s2 > 0x80000000);
3066}
3067
3068static bool serial_number_greater(uint32_t s1, uint32_t s2)
3069{
3070 return (s1 < s2 && s2 - s1 > 0x80000000) || (s1 > s2 && s1 - s2 < 0x80000000);
3071}
3072
3073
3074#ifdef IMPLEMENT_TARGET_AUTH
3075static void chap_md5_generate_challenge(uint8_t *pbChallenge, size_t *pcbChallenge)
3076{
3077 uint8_t cbChallenge;
3078
3079 cbChallenge = RTrand_U8(CHAP_MD5_CHALLENGE_MIN, CHAP_MD5_CHALLENGE_MAX);
3080 RTrand_bytes(pbChallenge, cbChallenge);
3081 *pcbChallenge = cbChallenge;
3082}
3083#endif
3084
3085
3086static void chap_md5_compute_response(uint8_t *pbResponse, uint8_t id, const uint8_t *pbChallenge, size_t cbChallenge,
3087 const uint8_t *pbSecret, size_t cbSecret)
3088{
3089 RTMD5CONTEXT ctx;
3090 uint8_t bId;
3091
3092 bId = id;
3093 RTMd5Init(&ctx);
3094 RTMd5Update(&ctx, &bId, 1);
3095 RTMd5Update(&ctx, pbSecret, cbSecret);
3096 RTMd5Update(&ctx, pbChallenge, cbChallenge);
3097 RTMd5Final(pbResponse, &ctx);
3098}
3099
3100/**
3101 * Internal. - Wrapper around the extended select callback of the net interface.
3102 */
3103DECLINLINE(int) iscsiIoThreadWait(PISCSIIMAGE pImage, RTMSINTERVAL cMillies, uint32_t fEvents, uint32_t *pfEvents)
3104{
3105 return pImage->pInterfaceNetCallbacks->pfnSelectOneEx(pImage->Socket, fEvents, pfEvents, cMillies);
3106}
3107
3108/**
3109 * Internal. - Pokes a thread waiting for I/O.
3110 */
3111DECLINLINE(int) iscsiIoThreadPoke(PISCSIIMAGE pImage)
3112{
3113 return pImage->pInterfaceNetCallbacks->pfnPoke(pImage->Socket);
3114}
3115
3116/**
3117 * Internal. - Get the next request from the queue.
3118 */
3119DECLINLINE(PISCSICMD) iscsiCmdGet(PISCSIIMAGE pImage)
3120{
3121 int rc;
3122 PISCSICMD pIScsiCmd = NULL;
3123
3124 rc = RTSemMutexRequest(pImage->MutexReqQueue, RT_INDEFINITE_WAIT);
3125 AssertRC(rc);
3126
3127 pIScsiCmd = pImage->pScsiReqQueue;
3128 if (pIScsiCmd)
3129 {
3130 pImage->pScsiReqQueue = pIScsiCmd->pNext;
3131 pIScsiCmd->pNext = NULL;
3132 }
3133
3134 rc = RTSemMutexRelease(pImage->MutexReqQueue);
3135 AssertRC(rc);
3136
3137 return pIScsiCmd;
3138}
3139
3140
3141/**
3142 * Internal. - Adds the given command to the queue.
3143 */
3144DECLINLINE(int) iscsiCmdPut(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd)
3145{
3146 int rc = RTSemMutexRequest(pImage->MutexReqQueue, RT_INDEFINITE_WAIT);
3147 AssertRC(rc);
3148
3149 pIScsiCmd->pNext = pImage->pScsiReqQueue;
3150 pImage->pScsiReqQueue = pIScsiCmd;
3151
3152 rc = RTSemMutexRelease(pImage->MutexReqQueue);
3153 AssertRC(rc);
3154
3155 iscsiIoThreadPoke(pImage);
3156
3157 return rc;
3158}
3159
3160/**
3161 * Internal. - Completes the request with the appropriate action.
3162 * Synchronous requests are completed with waking up the thread
3163 * and asynchronous ones by continuing the associated I/O context.
3164 */
3165static void iscsiCmdComplete(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd, int rcCmd)
3166{
3167 LogFlowFunc(("pImage=%#p pIScsiCmd=%#p rcCmd=%Rrc\n", pImage, pIScsiCmd, rcCmd));
3168
3169 /* Remove from the table first. */
3170 iscsiCmdRemove(pImage, pIScsiCmd->Itt);
3171
3172 /* Call completion callback. */
3173 pIScsiCmd->pfnComplete(pImage, rcCmd, pIScsiCmd->pvUser);
3174
3175 /* Free command structure. */
3176#ifdef DEBUG
3177 memset(pIScsiCmd, 0xff, sizeof(ISCSICMD));
3178#endif
3179 RTMemFree(pIScsiCmd);
3180}
3181
3182/**
3183 * Reattaches the to the target after an error aborting
3184 * pending commands and resending them.
3185 *
3186 * @param pImage iSCSI connection state.
3187 */
3188static void iscsiReattach(PISCSIIMAGE pImage)
3189{
3190 int rc = VINF_SUCCESS;
3191 PISCSICMD pIScsiCmdHead = NULL;
3192 PISCSICMD pIScsiCmd = NULL;
3193 PISCSICMD pIScsiCmdCur = NULL;
3194 PISCSIPDUTX pIScsiPDUTx = NULL;
3195
3196 /* Close connection. */
3197 iscsiTransportClose(pImage);
3198 pImage->state = ISCSISTATE_FREE;
3199
3200 /* Reset PDU we are receiving. */
3201 iscsiRecvPDUReset(pImage);
3202
3203 /*
3204 * Abort all PDUs we are about to transmit,
3205 * the command need a new Itt if the relogin is successful.
3206 */
3207 while (pImage->pIScsiPDUTxHead)
3208 {
3209 pIScsiPDUTx = pImage->pIScsiPDUTxHead;
3210 pImage->pIScsiPDUTxHead = pIScsiPDUTx->pNext;
3211
3212 pIScsiCmd = pIScsiPDUTx->pIScsiCmd;
3213
3214 if (pIScsiCmd)
3215 {
3216 /* Place on command list. */
3217 pIScsiCmd->pNext = pIScsiCmdHead;
3218 pIScsiCmdHead = pIScsiCmd;
3219 }
3220 RTMemFree(pIScsiPDUTx);
3221 }
3222
3223 /* Clear the current PDU too. */
3224 if (pImage->pIScsiPDUTxCur)
3225 {
3226 pIScsiPDUTx = pImage->pIScsiPDUTxCur;
3227
3228 pImage->pIScsiPDUTxCur = NULL;
3229 pIScsiCmd = pIScsiPDUTx->pIScsiCmd;
3230
3231 if (pIScsiCmd)
3232 {
3233 pIScsiCmd->pNext = pIScsiCmdHead;
3234 pIScsiCmdHead = pIScsiCmd;
3235 }
3236 RTMemFree(pIScsiPDUTx);
3237 }
3238
3239 /*
3240 * Get all commands which are waiting for a response
3241 * They need to be resend too after a successful reconnect.
3242 */
3243 pIScsiCmd = iscsiCmdRemoveAll(pImage);
3244
3245 if (pIScsiCmd)
3246 {
3247 pIScsiCmdCur = pIScsiCmd;
3248 while (pIScsiCmdCur->pNext)
3249 pIScsiCmdCur = pIScsiCmdCur->pNext;
3250
3251 /*
3252 * Place them in front of the list because they are the oldest requests
3253 * and need to be processed first to minimize the risk to time out.
3254 */
3255 pIScsiCmdCur->pNext = pIScsiCmdHead;
3256 pIScsiCmdHead = pIScsiCmd;
3257 }
3258
3259 /* Try to attach. */
3260 rc = iscsiAttach(pImage);
3261 if (RT_SUCCESS(rc))
3262 {
3263 /* Phew, we have a connection again.
3264 * Prepare new PDUs for the aborted commands.
3265 */
3266 while (pIScsiCmdHead)
3267 {
3268 pIScsiCmd = pIScsiCmdHead;
3269 pIScsiCmdHead = pIScsiCmdHead->pNext;
3270
3271 pIScsiCmd->pNext = NULL;
3272
3273 rc = iscsiPDUTxPrepare(pImage, pIScsiCmd);
3274 AssertRC(rc);
3275 }
3276 }
3277 else
3278 {
3279 /*
3280 * Still no luck, complete commands with error so the caller
3281 * has a chance to inform the user and maybe resend the command.
3282 */
3283 while (pIScsiCmdHead)
3284 {
3285 pIScsiCmd = pIScsiCmdHead;
3286 pIScsiCmdHead = pIScsiCmdHead->pNext;
3287
3288 iscsiCmdComplete(pImage, pIScsiCmd, VERR_BROKEN_PIPE);
3289 }
3290 }
3291}
3292
3293/**
3294 * Internal. Main iSCSI I/O worker.
3295 */
3296static DECLCALLBACK(int) iscsiIoThreadWorker(RTTHREAD ThreadSelf, void *pvUser)
3297{
3298 PISCSIIMAGE pImage = (PISCSIIMAGE)pvUser;
3299
3300 /* Initialize the initial event mask. */
3301 pImage->fPollEvents = VD_INTERFACETCPNET_EVT_READ | VD_INTERFACETCPNET_EVT_ERROR;
3302
3303 while (pImage->fRunning)
3304 {
3305 uint32_t fEvents;
3306 int rc;
3307
3308 fEvents = 0;
3309
3310 /* Wait for work or for data from the target. */
3311 RTMSINTERVAL msWait;
3312
3313 if (pImage->cCmdsWaiting)
3314 {
3315 pImage->fPollEvents &= ~VD_INTERFACETCPNET_HINT_INTERRUPT;
3316 msWait = pImage->uReadTimeout;
3317 }
3318 else
3319 {
3320 pImage->fPollEvents |= VD_INTERFACETCPNET_HINT_INTERRUPT;
3321 msWait = RT_INDEFINITE_WAIT;
3322 }
3323
3324 LogFlow(("Waiting for events fPollEvents=%#x\n", pImage->fPollEvents));
3325 rc = iscsiIoThreadWait(pImage, msWait, pImage->fPollEvents, &fEvents);
3326 if (rc == VERR_INTERRUPTED)
3327 {
3328 /* Check the queue. */
3329 PISCSICMD pIScsiCmd = iscsiCmdGet(pImage);
3330
3331 while (pIScsiCmd)
3332 {
3333 switch (pIScsiCmd->enmCmdType)
3334 {
3335 case ISCSICMDTYPE_REQ:
3336 {
3337 rc = iscsiPDUTxPrepare(pImage, pIScsiCmd);
3338 AssertRC(rc);
3339 break;
3340 }
3341 case ISCSICMDTYPE_EXEC:
3342 {
3343 rc = pIScsiCmd->CmdType.Exec.pfnExec(pIScsiCmd->CmdType.Exec.pvUser);
3344 iscsiCmdComplete(pImage, pIScsiCmd, rc);
3345 break;
3346 }
3347 default:
3348 AssertMsgFailed(("Invalid command type %d\n", pIScsiCmd->enmCmdType));
3349 }
3350
3351 pIScsiCmd = iscsiCmdGet(pImage);
3352 }
3353 }
3354 else if (rc == VERR_TIMEOUT && pImage->cCmdsWaiting)
3355 {
3356 /*
3357 * We are waiting for a response from the target but
3358 * it didn't answered yet.
3359 * We assume the connection is broken and try to reconnect.
3360 */
3361 LogFlow(("Timed out while waiting for an answer from the target, reconnecting\n"));
3362 iscsiReattach(pImage);
3363 }
3364 else if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT)
3365 {
3366 Assert(pImage->state == ISCSISTATE_NORMAL);
3367 LogFlow(("Got socket events %#x\n", fEvents));
3368
3369 if (fEvents & VD_INTERFACETCPNET_EVT_READ)
3370 {
3371 /* Continue or start a new PDU receive task */
3372 LogFlow(("There is data on the socket\n"));
3373 rc = iscsiRecvPDUAsync(pImage);
3374 if (rc == VERR_BROKEN_PIPE)
3375 iscsiReattach(pImage);
3376 else if (RT_FAILURE(rc))
3377 LogRel(("iSCSI: Handling incoming request failed %Rrc\n", rc));
3378 }
3379
3380 if (fEvents & VD_INTERFACETCPNET_EVT_WRITE)
3381 {
3382 LogFlow(("The socket is writable\n"));
3383 rc = iscsiSendPDUAsync(pImage);
3384 if (RT_FAILURE(rc))
3385 LogRel(("iSCSI: Sending PDU failed %Rrc\n", rc));
3386 }
3387
3388 if (fEvents & VD_INTERFACETCPNET_EVT_ERROR)
3389 {
3390 LogFlow(("An error ocurred\n"));
3391 iscsiReattach(pImage);
3392 }
3393 }
3394 else
3395 {
3396 LogRel(("iSCSI: Waiting for I/O failed rc=%Rrc\n", rc));
3397 }
3398 }
3399
3400 return VINF_SUCCESS;
3401}
3402
3403/**
3404 * Internal. - Enqueues a request asynchronously.
3405 */
3406static int iscsiCommandAsync(PISCSIIMAGE pImage, PSCSIREQ pScsiReq,
3407 PFNISCSICMDCOMPLETED pfnComplete, void *pvUser)
3408{
3409 int rc;
3410
3411 if (pImage->fExtendedSelectSupported)
3412 {
3413 PISCSICMD pIScsiCmd = (PISCSICMD)RTMemAllocZ(sizeof(ISCSICMD));
3414 if (!pIScsiCmd)
3415 return VERR_NO_MEMORY;
3416
3417 /* Init the command structure. */
3418 pIScsiCmd->pNext = NULL;
3419 pIScsiCmd->enmCmdType = ISCSICMDTYPE_REQ;
3420 pIScsiCmd->pfnComplete = pfnComplete;
3421 pIScsiCmd->pvUser = pvUser;
3422 pIScsiCmd->CmdType.ScsiReq.pScsiReq = pScsiReq;
3423
3424 rc = iscsiCmdPut(pImage, pIScsiCmd);
3425 if (RT_FAILURE(rc))
3426 RTMemFree(pIScsiCmd);
3427 }
3428 else
3429 rc = VERR_NOT_SUPPORTED;
3430
3431 return rc;
3432}
3433
3434static void iscsiCommandCompleteSync(PISCSIIMAGE pImage, int rcReq, void *pvUser)
3435{
3436 PISCSICMDSYNC pIScsiCmdSync = (PISCSICMDSYNC)pvUser;
3437
3438 pIScsiCmdSync->rcCmd = rcReq;
3439 int rc = RTSemEventSignal(pIScsiCmdSync->EventSem);
3440 AssertRC(rc);
3441}
3442
3443/**
3444 * Internal. - Enqueues a request in a synchronous fashion
3445 * i.e. returns when the request completed.
3446 */
3447static int iscsiCommandSync(PISCSIIMAGE pImage, PSCSIREQ pScsiReq, bool fRetry, int rcSense)
3448{
3449 int rc;
3450
3451 if (pImage->fExtendedSelectSupported)
3452 {
3453 ISCSICMDSYNC IScsiCmdSync;
3454
3455 /* Create event semaphore. */
3456 rc = RTSemEventCreate(&IScsiCmdSync.EventSem);
3457 if (RT_FAILURE(rc))
3458 return rc;
3459
3460 if (fRetry)
3461 {
3462 for (unsigned i = 0; i < 10; i++)
3463 {
3464 rc = iscsiCommandAsync(pImage, pScsiReq, iscsiCommandCompleteSync, &IScsiCmdSync);
3465 if (RT_FAILURE(rc))
3466 break;
3467
3468 rc = RTSemEventWait(IScsiCmdSync.EventSem, RT_INDEFINITE_WAIT);
3469 AssertRC(rc);
3470 rc = IScsiCmdSync.rcCmd;
3471
3472 if ( (RT_SUCCESS(rc) && !pScsiReq->cbSense)
3473 || RT_FAILURE(rc))
3474 break;
3475 rc = rcSense;
3476 }
3477 }
3478 else
3479 {
3480 rc = iscsiCommandAsync(pImage, pScsiReq, iscsiCommandCompleteSync, &IScsiCmdSync);
3481 if (RT_SUCCESS(rc))
3482 {
3483 rc = RTSemEventWait(IScsiCmdSync.EventSem, RT_INDEFINITE_WAIT);
3484 AssertRC(rc);
3485 rc = IScsiCmdSync.rcCmd;
3486
3487 if (RT_FAILURE(rc) || pScsiReq->cbSense > 0)
3488 rc = rcSense;
3489 }
3490 }
3491
3492 RTSemEventDestroy(IScsiCmdSync.EventSem);
3493 }
3494 else
3495 {
3496 if (fRetry)
3497 {
3498 for (unsigned i = 0; i < 10; i++)
3499 {
3500 rc = iscsiCommand(pImage, pScsiReq);
3501 if ( (RT_SUCCESS(rc) && !pScsiReq->cbSense)
3502 || RT_FAILURE(rc))
3503 break;
3504 rc = rcSense;
3505 }
3506 }
3507 else
3508 {
3509 rc = iscsiCommand(pImage, pScsiReq);
3510 if (RT_SUCCESS(rc) && pScsiReq->cbSense > 0)
3511 rc = rcSense;
3512 }
3513 }
3514
3515 return rc;
3516}
3517
3518
3519/**
3520 * Internal. - Executes a given function in a synchronous fashion
3521 * on the I/O thread if available.
3522 */
3523static int iscsiExecSync(PISCSIIMAGE pImage, PFNISCSIEXEC pfnExec, void *pvUser)
3524{
3525 int rc;
3526
3527 if (pImage->fExtendedSelectSupported)
3528 {
3529 ISCSICMDSYNC IScsiCmdSync;
3530 PISCSICMD pIScsiCmd = (PISCSICMD)RTMemAllocZ(sizeof(ISCSICMD));
3531 if (!pIScsiCmd)
3532 return VERR_NO_MEMORY;
3533
3534 /* Create event semaphore. */
3535 rc = RTSemEventCreate(&IScsiCmdSync.EventSem);
3536 if (RT_FAILURE(rc))
3537 {
3538 RTMemFree(pIScsiCmd);
3539 return rc;
3540 }
3541
3542 /* Init the command structure. */
3543 pIScsiCmd->pNext = NULL;
3544 pIScsiCmd->enmCmdType = ISCSICMDTYPE_EXEC;
3545 pIScsiCmd->pfnComplete = iscsiCommandCompleteSync;
3546 pIScsiCmd->pvUser = &IScsiCmdSync;
3547 pIScsiCmd->CmdType.Exec.pfnExec = pfnExec;
3548 pIScsiCmd->CmdType.Exec.pvUser = pvUser;
3549
3550 rc = iscsiCmdPut(pImage, pIScsiCmd);
3551 if (RT_FAILURE(rc))
3552 RTMemFree(pIScsiCmd);
3553 else
3554 {
3555 rc = RTSemEventWait(IScsiCmdSync.EventSem, RT_INDEFINITE_WAIT);
3556 AssertRC(rc);
3557 rc = IScsiCmdSync.rcCmd;
3558 }
3559
3560 RTSemEventDestroy(IScsiCmdSync.EventSem);
3561 }
3562 else
3563 {
3564 /* No I/O thread, execute in the current thread. */
3565 rc = pfnExec(pvUser);
3566 }
3567
3568 return rc;
3569}
3570
3571
3572static void iscsiCommandAsyncComplete(PISCSIIMAGE pImage, int rcReq, void *pvUser)
3573{
3574 bool fComplete = true;
3575 size_t cbTransfered = 0;
3576 PSCSIREQASYNC pReqAsync = (PSCSIREQASYNC)pvUser;
3577 PSCSIREQ pScsiReq = pReqAsync->pScsiReq;
3578
3579 if ( RT_SUCCESS(rcReq)
3580 && pScsiReq->cbSense > 0)
3581 {
3582 /* Try again if possible. */
3583 if (pReqAsync->cSenseRetries > 0)
3584 {
3585 pReqAsync->cSenseRetries--;
3586 pScsiReq->cbSense = sizeof(pReqAsync->abSense);
3587 int rc = iscsiCommandAsync(pImage, pScsiReq, iscsiCommandAsyncComplete, pReqAsync);
3588 if (RT_SUCCESS(rc))
3589 fComplete = false;
3590 else
3591 rcReq = pReqAsync->rcSense;
3592 }
3593 else
3594 rcReq = pReqAsync->rcSense;
3595 }
3596
3597 if (fComplete)
3598 {
3599 if (pScsiReq->enmXfer == SCSIXFER_FROM_TARGET)
3600 cbTransfered = pScsiReq->cbT2IData;
3601 else if (pScsiReq->enmXfer == SCSIXFER_TO_TARGET)
3602 cbTransfered = pScsiReq->cbI2TData;
3603 else
3604 AssertMsg(pScsiReq->enmXfer == SCSIXFER_NONE, ("To/From transfers are not supported yet\n"));
3605
3606 /* Continue I/O context. */
3607 pImage->pInterfaceIoCallbacks->pfnIoCtxCompleted(pImage->pInterfaceIo->pvUser,
3608 pReqAsync->pIoCtx, rcReq,
3609 cbTransfered);
3610
3611 RTMemFree(pScsiReq);
3612 RTMemFree(pReqAsync);
3613 }
3614}
3615
3616
3617/**
3618 * Internal. Free all allocated space for representing an image, and optionally
3619 * delete the image from disk.
3620 */
3621static int iscsiFreeImage(PISCSIIMAGE pImage, bool fDelete)
3622{
3623 int rc = VINF_SUCCESS;
3624 Assert(!fDelete); /* This MUST be false, the flag isn't supported. */
3625
3626 /* Freeing a never allocated image (e.g. because the open failed) is
3627 * not signalled as an error. After all nothing bad happens. */
3628 if (pImage)
3629 {
3630 if (pImage->Mutex != NIL_RTSEMMUTEX)
3631 {
3632 /* Detaching only makes sense when the mutex is there. Otherwise the
3633 * failure happened long before we could attach to the target. */
3634 iscsiExecSync(pImage, iscsiDetach, pImage);
3635 RTSemMutexDestroy(pImage->Mutex);
3636 pImage->Mutex = NIL_RTSEMMUTEX;
3637 }
3638 if (pImage->hThreadIo != NIL_RTTHREAD)
3639 {
3640 ASMAtomicXchgBool(&pImage->fRunning, false);
3641 rc = iscsiIoThreadPoke(pImage);
3642 AssertRC(rc);
3643
3644 /* Wait for the thread to terminate. */
3645 rc = RTThreadWait(pImage->hThreadIo, RT_INDEFINITE_WAIT, NULL);
3646 AssertRC(rc);
3647 }
3648 /* Destroy the socket. */
3649 if (pImage->Socket != NIL_VDSOCKET)
3650 {
3651 pImage->pInterfaceNetCallbacks->pfnSocketDestroy(pImage->Socket);
3652 }
3653 if (pImage->MutexReqQueue != NIL_RTSEMMUTEX)
3654 {
3655 RTSemMutexDestroy(pImage->MutexReqQueue);
3656 pImage->MutexReqQueue = NIL_RTSEMMUTEX;
3657 }
3658 if (pImage->pszTargetName)
3659 {
3660 RTMemFree(pImage->pszTargetName);
3661 pImage->pszTargetName = NULL;
3662 }
3663 if (pImage->pszInitiatorName)
3664 {
3665 if (pImage->fAutomaticInitiatorName)
3666 RTStrFree(pImage->pszInitiatorName);
3667 else
3668 RTMemFree(pImage->pszInitiatorName);
3669 pImage->pszInitiatorName = NULL;
3670 }
3671 if (pImage->pszInitiatorUsername)
3672 {
3673 RTMemFree(pImage->pszInitiatorUsername);
3674 pImage->pszInitiatorUsername = NULL;
3675 }
3676 if (pImage->pbInitiatorSecret)
3677 {
3678 RTMemFree(pImage->pbInitiatorSecret);
3679 pImage->pbInitiatorSecret = NULL;
3680 }
3681 if (pImage->pszTargetUsername)
3682 {
3683 RTMemFree(pImage->pszTargetUsername);
3684 pImage->pszTargetUsername = NULL;
3685 }
3686 if (pImage->pbTargetSecret)
3687 {
3688 RTMemFree(pImage->pbTargetSecret);
3689 pImage->pbTargetSecret = NULL;
3690 }
3691 if (pImage->pvRecvPDUBuf)
3692 {
3693 RTMemFree(pImage->pvRecvPDUBuf);
3694 pImage->pvRecvPDUBuf = NULL;
3695 }
3696 }
3697
3698 LogFlowFunc(("returns %Rrc\n", rc));
3699 return rc;
3700}
3701
3702/**
3703 * Internal: Open an image, constructing all necessary data structures.
3704 */
3705static int iscsiOpenImage(PISCSIIMAGE pImage, unsigned uOpenFlags)
3706{
3707 int rc;
3708 char *pszLUN = NULL, *pszLUNInitial = NULL;
3709 bool fLunEncoded = false;
3710 uint32_t uWriteSplitDef = 0;
3711 uint32_t uTimeoutDef = 0;
3712 uint64_t uHostIPTmp = 0;
3713 bool fHostIPDef = 0;
3714 rc = RTStrToUInt32Full(s_iscsiConfigDefaultWriteSplit, 0, &uWriteSplitDef);
3715 AssertRC(rc);
3716 rc = RTStrToUInt32Full(s_iscsiConfigDefaultTimeout, 0, &uTimeoutDef);
3717 AssertRC(rc);
3718 rc = RTStrToUInt64Full(s_iscsiConfigDefaultHostIPStack, 0, &uHostIPTmp);
3719 AssertRC(rc);
3720 fHostIPDef = !!uHostIPTmp;
3721
3722#if 0
3723 if (uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO)
3724 return VERR_NOT_SUPPORTED;
3725#endif
3726
3727 pImage->uOpenFlags = uOpenFlags;
3728
3729 /* Get error signalling interface. */
3730 pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
3731 if (pImage->pInterfaceError)
3732 pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
3733
3734 /* Get TCP network stack interface. */
3735 pImage->pInterfaceNet = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_TCPNET);
3736 if (pImage->pInterfaceNet)
3737 pImage->pInterfaceNetCallbacks = VDGetInterfaceTcpNet(pImage->pInterfaceNet);
3738 else
3739 {
3740 rc = iscsiError(pImage, VERR_VD_ISCSI_UNKNOWN_INTERFACE,
3741 RT_SRC_POS, N_("iSCSI: TCP network stack interface missing"));
3742 goto out;
3743 }
3744
3745 /* Get configuration interface. */
3746 pImage->pInterfaceConfig = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_CONFIG);
3747 if (pImage->pInterfaceConfig)
3748 pImage->pInterfaceConfigCallbacks = VDGetInterfaceConfig(pImage->pInterfaceConfig);
3749 else
3750 {
3751 rc = iscsiError(pImage, VERR_VD_ISCSI_UNKNOWN_INTERFACE,
3752 RT_SRC_POS, N_("iSCSI: configuration interface missing"));
3753 goto out;
3754 }
3755
3756 /* Get I/O interface. */
3757 pImage->pInterfaceIo = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
3758 if (pImage->pInterfaceIo)
3759 pImage->pInterfaceIoCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIo);
3760 else
3761 {
3762 rc = iscsiError(pImage, VERR_VD_ISCSI_UNKNOWN_INTERFACE,
3763 RT_SRC_POS, N_("iSCSI: I/O interface missing"));
3764 goto out;
3765 }
3766
3767 /* This ISID will be adjusted later to make it unique on this host. */
3768 pImage->ISID = 0x800000000000ULL | 0x001234560000ULL;
3769 pImage->cISCSIRetries = 10;
3770 pImage->state = ISCSISTATE_FREE;
3771 pImage->pvRecvPDUBuf = RTMemAlloc(ISCSI_RECV_PDU_BUFFER_SIZE);
3772 pImage->cbRecvPDUBuf = ISCSI_RECV_PDU_BUFFER_SIZE;
3773 if (pImage->pvRecvPDUBuf == NULL)
3774 {
3775 rc = VERR_NO_MEMORY;
3776 goto out;
3777 }
3778 pImage->Mutex = NIL_RTSEMMUTEX;
3779 pImage->MutexReqQueue = NIL_RTSEMMUTEX;
3780 rc = RTSemMutexCreate(&pImage->Mutex);
3781 if (RT_FAILURE(rc))
3782 goto out;
3783
3784 rc = RTSemMutexCreate(&pImage->MutexReqQueue);
3785 if (RT_FAILURE(rc))
3786 goto out;
3787
3788 /* Validate configuration, detect unknown keys. */
3789 if (!VDCFGAreKeysValid(pImage->pInterfaceConfigCallbacks,
3790 pImage->pInterfaceConfig->pvUser,
3791 "TargetName\0InitiatorName\0LUN\0TargetAddress\0InitiatorUsername\0InitiatorSecret\0TargetUsername\0TargetSecret\0WriteSplit\0Timeout\0HostIPStack\0"))
3792 {
3793 rc = iscsiError(pImage, VERR_VD_ISCSI_UNKNOWN_CFG_VALUES, RT_SRC_POS, N_("iSCSI: configuration error: unknown configuration keys present"));
3794 goto out;
3795 }
3796
3797 /* Query the iSCSI upper level configuration. */
3798 rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
3799 pImage->pInterfaceConfig->pvUser,
3800 "TargetName", &pImage->pszTargetName);
3801 if (RT_FAILURE(rc))
3802 {
3803 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetName as string"));
3804 goto out;
3805 }
3806 rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
3807 pImage->pInterfaceConfig->pvUser,
3808 "InitiatorName", &pImage->pszInitiatorName);
3809 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
3810 {
3811 pImage->fAutomaticInitiatorName = true;
3812 rc = VINF_SUCCESS;
3813 }
3814 if (RT_FAILURE(rc))
3815 {
3816 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorName as string"));
3817 goto out;
3818 }
3819 rc = VDCFGQueryStringAllocDef(pImage->pInterfaceConfigCallbacks,
3820 pImage->pInterfaceConfig->pvUser,
3821 "LUN", &pszLUN, s_iscsiConfigDefaultLUN);
3822 if (RT_FAILURE(rc))
3823 {
3824 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read LUN as string"));
3825 goto out;
3826 }
3827 pszLUNInitial = pszLUN;
3828 if (!strncmp(pszLUN, "enc", 3))
3829 {
3830 fLunEncoded = true;
3831 pszLUN += 3;
3832 }
3833 rc = RTStrToUInt64Full(pszLUN, 0, &pImage->LUN);
3834 if (RT_FAILURE(rc))
3835 {
3836 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to convert LUN to integer"));
3837 goto out;
3838 }
3839 if (!fLunEncoded)
3840 {
3841 if (pImage->LUN <= 255)
3842 {
3843 pImage->LUN = pImage->LUN << 48; /* uses peripheral device addressing method */
3844 }
3845 else if (pImage->LUN <= 16383)
3846 {
3847 pImage->LUN = (pImage->LUN << 48) | RT_BIT_64(62); /* uses flat space addressing method */
3848 }
3849 else
3850 {
3851 rc = VERR_OUT_OF_RANGE;
3852 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: LUN number out of range (0-16383)"));
3853 goto out;
3854 }
3855 }
3856 rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
3857 pImage->pInterfaceConfig->pvUser,
3858 "TargetAddress", &pImage->pszTargetAddress);
3859 if (RT_FAILURE(rc))
3860 {
3861 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetAddress as string"));
3862 goto out;
3863 }
3864 pImage->pszInitiatorUsername = NULL;
3865 rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
3866 pImage->pInterfaceConfig->pvUser,
3867 "InitiatorUsername",
3868 &pImage->pszInitiatorUsername);
3869 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
3870 rc = VINF_SUCCESS;
3871 if (RT_FAILURE(rc))
3872 {
3873 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorUsername as string"));
3874 goto out;
3875 }
3876 pImage->pbInitiatorSecret = NULL;
3877 pImage->cbInitiatorSecret = 0;
3878 rc = VDCFGQueryBytesAlloc(pImage->pInterfaceConfigCallbacks,
3879 pImage->pInterfaceConfig->pvUser,
3880 "InitiatorSecret",
3881 (void **)&pImage->pbInitiatorSecret,
3882 &pImage->cbInitiatorSecret);
3883 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
3884 rc = VINF_SUCCESS;
3885 if (RT_FAILURE(rc))
3886 {
3887 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorSecret as byte string"));
3888 goto out;
3889 }
3890 pImage->pszTargetUsername = NULL;
3891 rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
3892 pImage->pInterfaceConfig->pvUser,
3893 "TargetUsername",
3894 &pImage->pszTargetUsername);
3895 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
3896 rc = VINF_SUCCESS;
3897 if (RT_FAILURE(rc))
3898 {
3899 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetUsername as string"));
3900 goto out;
3901 }
3902 pImage->pbTargetSecret = NULL;
3903 pImage->cbTargetSecret = 0;
3904 rc = VDCFGQueryBytesAlloc(pImage->pInterfaceConfigCallbacks,
3905 pImage->pInterfaceConfig->pvUser,
3906 "TargetSecret", (void **)&pImage->pbTargetSecret,
3907 &pImage->cbTargetSecret);
3908 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
3909 rc = VINF_SUCCESS;
3910 if (RT_FAILURE(rc))
3911 {
3912 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetSecret as byte string"));
3913 goto out;
3914 }
3915 rc = VDCFGQueryU32Def(pImage->pInterfaceConfigCallbacks,
3916 pImage->pInterfaceConfig->pvUser,
3917 "WriteSplit", &pImage->cbWriteSplit,
3918 uWriteSplitDef);
3919 if (RT_FAILURE(rc))
3920 {
3921 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read WriteSplit as U32"));
3922 goto out;
3923 }
3924
3925 pImage->pszHostname = NULL;
3926 pImage->uPort = 0;
3927 pImage->Socket = NIL_VDSOCKET;
3928 /* Query the iSCSI lower level configuration. */
3929 rc = VDCFGQueryU32Def(pImage->pInterfaceConfigCallbacks,
3930 pImage->pInterfaceConfig->pvUser,
3931 "Timeout", &pImage->uReadTimeout,
3932 uTimeoutDef);
3933 if (RT_FAILURE(rc))
3934 {
3935 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read Timeout as U32"));
3936 goto out;
3937 }
3938 rc = VDCFGQueryBoolDef(pImage->pInterfaceConfigCallbacks,
3939 pImage->pInterfaceConfig->pvUser,
3940 "HostIPStack", &pImage->fHostIP,
3941 fHostIPDef);
3942 if (RT_FAILURE(rc))
3943 {
3944 rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read HostIPStack as boolean"));
3945 goto out;
3946 }
3947
3948 /* Don't actually establish iSCSI transport connection if this is just an
3949 * open to query the image information and the host IP stack isn't used.
3950 * Even trying is rather useless, as in this context the InTnet IP stack
3951 * isn't present. Returning dummies is the best possible result anyway. */
3952 if ((uOpenFlags & VD_OPEN_FLAGS_INFO) && !pImage->fHostIP)
3953 {
3954 LogFunc(("Not opening the transport connection as IntNet IP stack is not available. Will return dummies\n"));
3955 goto out;
3956 }
3957
3958 memset(pImage->aCmdsWaiting, 0, sizeof(pImage->aCmdsWaiting));
3959
3960 /* Create the socket structure. */
3961 rc = pImage->pInterfaceNetCallbacks->pfnSocketCreate(VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT,
3962 &pImage->Socket);
3963 if (RT_SUCCESS(rc))
3964 {
3965 pImage->fExtendedSelectSupported = true;
3966 pImage->fRunning = true;
3967 rc = RTThreadCreate(&pImage->hThreadIo, iscsiIoThreadWorker, pImage, 0,
3968 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "iSCSI-Io");
3969 if (RT_FAILURE(rc))
3970 {
3971 LogFunc(("Creating iSCSI I/O thread failed rc=%Rrc\n", rc));
3972 goto out;
3973 }
3974 }
3975 else if (rc == VERR_NOT_SUPPORTED)
3976 {
3977 /* Async I/O is not supported without extended select. */
3978 if ((uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO))
3979 {
3980 LogFunc(("Extended select is not supported by the interface but async I/O is requested -> %Rrc\n", rc));
3981 goto out;
3982 }
3983 else
3984 {
3985 pImage->fExtendedSelectSupported = false;
3986 rc = pImage->pInterfaceNetCallbacks->pfnSocketCreate(0, &pImage->Socket);
3987 if (RT_FAILURE(rc))
3988 {
3989 LogFunc(("Creating socket failed -> %Rrc\n", rc));
3990 goto out;
3991 }
3992 }
3993 }
3994 else
3995 {
3996 LogFunc(("Creating socket failed -> %Rrc\n", rc));
3997 goto out;
3998 }
3999
4000 /*
4001 * Attach to the iSCSI target. This implicitly establishes the iSCSI
4002 * transport connection.
4003 */
4004 rc = iscsiExecSync(pImage, iscsiAttach, pImage);
4005 if (RT_FAILURE(rc))
4006 {
4007 LogRel(("iSCSI: could not open target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
4008 goto out;
4009 }
4010 LogFlowFunc(("target '%s' opened successfully\n", pImage->pszTargetName));
4011
4012 SCSIREQ sr;
4013 RTSGSEG DataSeg;
4014 uint8_t sense[96];
4015 uint8_t data8[8];
4016 uint8_t data12[12];
4017
4018 /*
4019 * Inquire available LUNs - purely dummy request.
4020 */
4021 uint8_t CDB_rlun[12];
4022 uint8_t rlundata[16];
4023 CDB_rlun[0] = SCSI_REPORT_LUNS;
4024 CDB_rlun[1] = 0; /* reserved */
4025 CDB_rlun[2] = 0; /* reserved */
4026 CDB_rlun[3] = 0; /* reserved */
4027 CDB_rlun[4] = 0; /* reserved */
4028 CDB_rlun[5] = 0; /* reserved */
4029 CDB_rlun[6] = sizeof(rlundata) >> 24;
4030 CDB_rlun[7] = (sizeof(rlundata) >> 16) & 0xff;
4031 CDB_rlun[8] = (sizeof(rlundata) >> 8) & 0xff;
4032 CDB_rlun[9] = sizeof(rlundata) & 0xff;
4033 CDB_rlun[10] = 0; /* reserved */
4034 CDB_rlun[11] = 0; /* control */
4035
4036 DataSeg.pvSeg = rlundata;
4037 DataSeg.cbSeg = sizeof(rlundata);
4038
4039 sr.enmXfer = SCSIXFER_FROM_TARGET;
4040 sr.cbCDB = sizeof(CDB_rlun);
4041 sr.pvCDB = CDB_rlun;
4042 sr.cbI2TData = 0;
4043 sr.paI2TSegs = NULL;
4044 sr.cI2TSegs = 0;
4045 sr.cbT2IData = DataSeg.cbSeg;
4046 sr.paT2ISegs = &DataSeg;
4047 sr.cT2ISegs = 1;
4048 sr.cbSense = sizeof(sense);
4049 sr.pvSense = sense;
4050
4051 rc = iscsiCommandSync(pImage, &sr, false, VERR_INVALID_STATE);
4052 if (RT_FAILURE(rc))
4053 {
4054 LogRel(("iSCSI: Could not get LUN info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
4055 return rc;
4056 }
4057
4058 /*
4059 * Inquire device characteristics - no tapes, scanners etc., please.
4060 */
4061 uint8_t CDB_inq[6];
4062 CDB_inq[0] = SCSI_INQUIRY;
4063 CDB_inq[1] = 0; /* reserved */
4064 CDB_inq[2] = 0; /* reserved */
4065 CDB_inq[3] = 0; /* reserved */
4066 CDB_inq[4] = sizeof(data8);
4067 CDB_inq[5] = 0; /* control */
4068
4069 DataSeg.pvSeg = data8;
4070 DataSeg.cbSeg = sizeof(data8);
4071
4072 sr.enmXfer = SCSIXFER_FROM_TARGET;
4073 sr.cbCDB = sizeof(CDB_inq);
4074 sr.pvCDB = CDB_inq;
4075 sr.cbI2TData = 0;
4076 sr.paI2TSegs = NULL;
4077 sr.cI2TSegs = 0;
4078 sr.cbT2IData = DataSeg.cbSeg;
4079 sr.paT2ISegs = &DataSeg;
4080 sr.cT2ISegs = 1;
4081 sr.cbSense = sizeof(sense);
4082 sr.pvSense = sense;
4083
4084 rc = iscsiCommandSync(pImage, &sr, true /* fRetry */, VERR_INVALID_STATE);
4085 if (RT_SUCCESS(rc))
4086 {
4087 uint8_t devType = (sr.cbT2IData > 0) ? data8[0] & SCSI_DEVTYPE_MASK : 255;
4088 if (devType != SCSI_DEVTYPE_DISK)
4089 {
4090 rc = iscsiError(pImage, VERR_VD_ISCSI_INVALID_TYPE,
4091 RT_SRC_POS, N_("iSCSI: target address %s, target name %s, SCSI LUN %lld reports device type=%u"),
4092 pImage->pszTargetAddress, pImage->pszTargetName,
4093 pImage->LUN, devType);
4094 LogRel(("iSCSI: Unsupported SCSI peripheral device type %d for target %s\n", devType & SCSI_DEVTYPE_MASK, pImage->pszTargetName));
4095 goto out;
4096 }
4097 uint8_t uCmdQueue = (sr.cbT2IData >= 8) ? data8[7] & SCSI_INQUIRY_CMDQUE_MASK : 0;
4098 if (uCmdQueue > 0)
4099 pImage->fCmdQueuingSupported = true;
4100 else if (uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO)
4101 {
4102 rc = VERR_NOT_SUPPORTED;
4103 goto out;
4104 }
4105
4106 LogRel(("iSCSI: target address %s, target name %s, %s command queuing\n",
4107 pImage->pszTargetAddress, pImage->pszTargetName,
4108 pImage->fCmdQueuingSupported ? "supports" : "doesn't support"));
4109 }
4110 else
4111 {
4112 LogRel(("iSCSI: Could not get INQUIRY info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
4113 goto out;
4114 }
4115
4116 /*
4117 * Query write disable bit in the device specific parameter entry in the
4118 * mode parameter header. Refuse read/write opening of read only disks.
4119 */
4120
4121 uint8_t CDB_ms[6];
4122 uint8_t data4[4];
4123 CDB_ms[0] = SCSI_MODE_SENSE_6;
4124 CDB_ms[1] = 0; /* dbd=0/reserved */
4125 CDB_ms[2] = 0x3f; /* pc=0/page code=0x3f, ask for all pages */
4126 CDB_ms[3] = 0; /* subpage code=0, return everything in page_0 format */
4127 CDB_ms[4] = sizeof(data4); /* allocation length=4 */
4128 CDB_ms[5] = 0; /* control */
4129
4130 DataSeg.pvSeg = data4;
4131 DataSeg.cbSeg = sizeof(data4);
4132
4133 sr.enmXfer = SCSIXFER_FROM_TARGET;
4134 sr.cbCDB = sizeof(CDB_ms);
4135 sr.pvCDB = CDB_ms;
4136 sr.cbI2TData = 0;
4137 sr.paI2TSegs = NULL;
4138 sr.cI2TSegs = 0;
4139 sr.cbT2IData = DataSeg.cbSeg;
4140 sr.paT2ISegs = &DataSeg;
4141 sr.cT2ISegs = 1;
4142 sr.cbSense = sizeof(sense);
4143 sr.pvSense = sense;
4144
4145 rc = iscsiCommandSync(pImage, &sr, true /* fRetry */, VERR_INVALID_STATE);
4146 if (RT_SUCCESS(rc))
4147 {
4148 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY) && data4[2] & 0x80)
4149 {
4150 rc = VERR_VD_IMAGE_READ_ONLY;
4151 goto out;
4152 }
4153 }
4154 else
4155 {
4156 LogRel(("iSCSI: Could not get MODE SENSE info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
4157 goto out;
4158 }
4159
4160 /*
4161 * Determine sector size and capacity of the volume immediately.
4162 */
4163 uint8_t CDB_cap[16];
4164
4165 RT_ZERO(data12);
4166 RT_ZERO(CDB_cap);
4167 CDB_cap[0] = SCSI_SERVICE_ACTION_IN_16;
4168 CDB_cap[1] = SCSI_SVC_ACTION_IN_READ_CAPACITY_16; /* subcommand */
4169 CDB_cap[10+3] = sizeof(data12); /* allocation length (dword) */
4170
4171 DataSeg.pvSeg = data12;
4172 DataSeg.cbSeg = sizeof(data12);
4173
4174 sr.enmXfer = SCSIXFER_FROM_TARGET;
4175 sr.cbCDB = sizeof(CDB_cap);
4176 sr.pvCDB = CDB_cap;
4177 sr.cbI2TData = 0;
4178 sr.paI2TSegs = NULL;
4179 sr.cI2TSegs = 0;
4180 sr.cbT2IData = DataSeg.cbSeg;
4181 sr.paT2ISegs = &DataSeg;
4182 sr.cT2ISegs = 1;
4183 sr.cbSense = sizeof(sense);
4184 sr.pvSense = sense;
4185
4186 rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4187 if ( RT_SUCCESS(rc)
4188 && sr.status == SCSI_STATUS_OK)
4189 {
4190 pImage->cVolume = RT_BE2H_U64(*(uint64_t *)&data12[0]);
4191 pImage->cVolume++;
4192 pImage->cbSector = RT_BE2H_U32(*(uint32_t *)&data12[8]);
4193 pImage->cbSize = pImage->cVolume * pImage->cbSector;
4194 if (pImage->cVolume == 0 || pImage->cbSector != 512 || pImage->cbSize < pImage->cVolume)
4195 {
4196 rc = iscsiError(pImage, VERR_VD_ISCSI_INVALID_TYPE,
4197 RT_SRC_POS, N_("iSCSI: target address %s, target name %s, SCSI LUN %lld reports media sector count=%llu sector size=%u"),
4198 pImage->pszTargetAddress, pImage->pszTargetName,
4199 pImage->LUN, pImage->cVolume, pImage->cbSector);
4200 }
4201 }
4202 else
4203 {
4204 uint8_t CDB_capfb[10];
4205
4206 RT_ZERO(data8);
4207 CDB_capfb[0] = SCSI_READ_CAPACITY;
4208 CDB_capfb[1] = 0; /* reserved */
4209 CDB_capfb[2] = 0; /* reserved */
4210 CDB_capfb[3] = 0; /* reserved */
4211 CDB_capfb[4] = 0; /* reserved */
4212 CDB_capfb[5] = 0; /* reserved */
4213 CDB_capfb[6] = 0; /* reserved */
4214 CDB_capfb[7] = 0; /* reserved */
4215 CDB_capfb[8] = 0; /* reserved */
4216 CDB_capfb[9] = 0; /* control */
4217
4218 DataSeg.pvSeg = data8;
4219 DataSeg.cbSeg = sizeof(data8);
4220
4221 sr.enmXfer = SCSIXFER_FROM_TARGET;
4222 sr.cbCDB = sizeof(CDB_capfb);
4223 sr.pvCDB = CDB_capfb;
4224 sr.cbI2TData = 0;
4225 sr.paI2TSegs = NULL;
4226 sr.cI2TSegs = 0;
4227 sr.cbT2IData = DataSeg.cbSeg;
4228 sr.paT2ISegs = &DataSeg;
4229 sr.cT2ISegs = 1;
4230 sr.cbSense = sizeof(sense);
4231 sr.pvSense = sense;
4232
4233 rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4234 if (RT_SUCCESS(rc))
4235 {
4236 pImage->cVolume = (data8[0] << 24) | (data8[1] << 16) | (data8[2] << 8) | data8[3];
4237 pImage->cVolume++;
4238 pImage->cbSector = (data8[4] << 24) | (data8[5] << 16) | (data8[6] << 8) | data8[7];
4239 pImage->cbSize = pImage->cVolume * pImage->cbSector;
4240 if (pImage->cVolume == 0 || pImage->cbSector != 512)
4241 {
4242 rc = iscsiError(pImage, VERR_VD_ISCSI_INVALID_TYPE,
4243 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"),
4244 pImage->pszTargetAddress, pImage->pszTargetName,
4245 pImage->LUN, pImage->cVolume, pImage->cbSector);
4246 }
4247 }
4248 else
4249 {
4250 LogRel(("iSCSI: Could not determine capacity of target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
4251 goto out;
4252 }
4253 }
4254
4255 /*
4256 * Check the read and write cache bits.
4257 * Try to enable the cache if it is disabled.
4258 *
4259 * We already checked that this is a block access device. No need
4260 * to do it again.
4261 */
4262 uint8_t aCachingModePage[32];
4263 uint8_t aCDBModeSense6[6];
4264
4265 memset(aCachingModePage, '\0', sizeof(aCachingModePage));
4266 aCDBModeSense6[0] = SCSI_MODE_SENSE_6;
4267 aCDBModeSense6[1] = 0;
4268 aCDBModeSense6[2] = (0x00 << 6) | (0x08 & 0x3f); /* Current values and caching mode page */
4269 aCDBModeSense6[3] = 0; /* Sub page code. */
4270 aCDBModeSense6[4] = sizeof(aCachingModePage) & 0xff;
4271 aCDBModeSense6[5] = 0;
4272
4273 DataSeg.pvSeg = aCachingModePage;
4274 DataSeg.cbSeg = sizeof(aCachingModePage);
4275
4276 sr.enmXfer = SCSIXFER_FROM_TARGET;
4277 sr.cbCDB = sizeof(aCDBModeSense6);
4278 sr.pvCDB = aCDBModeSense6;
4279 sr.cbI2TData = 0;
4280 sr.paI2TSegs = NULL;
4281 sr.cI2TSegs = 0;
4282 sr.cbT2IData = DataSeg.cbSeg;
4283 sr.paT2ISegs = &DataSeg;
4284 sr.cT2ISegs = 1;
4285 sr.cbSense = sizeof(sense);
4286 sr.pvSense = sense;
4287 rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4288 if ( RT_SUCCESS(rc)
4289 && (sr.status == SCSI_STATUS_OK)
4290 && (aCachingModePage[0] >= 15)
4291 && (aCachingModePage[4 + aCachingModePage[3]] & 0x3f) == 0x08
4292 && (aCachingModePage[4 + aCachingModePage[3] + 1] > 3))
4293 {
4294 uint32_t Offset = 4 + aCachingModePage[3];
4295 /*
4296 * Check if the read and/or the write cache is disabled.
4297 * The write cache is disabled if bit 2 (WCE) is zero and
4298 * the read cache is disabled if bit 0 (RCD) is set.
4299 */
4300 if (!ASMBitTest(&aCachingModePage[Offset + 2], 2) || ASMBitTest(&aCachingModePage[Offset + 2], 0))
4301 {
4302 /*
4303 * Write Cache Enable (WCE) bit is zero or the Read Cache Disable (RCD) is one
4304 * So one of the caches is disabled. Enable both caches.
4305 * The rest is unchanged.
4306 */
4307 ASMBitSet(&aCachingModePage[Offset + 2], 2);
4308 ASMBitClear(&aCachingModePage[Offset + 2], 0);
4309
4310 uint8_t aCDBCaching[6];
4311 aCDBCaching[0] = SCSI_MODE_SELECT_6;
4312 aCDBCaching[1] = 0; /* Don't write the page into NV RAM. */
4313 aCDBCaching[2] = 0;
4314 aCDBCaching[3] = 0;
4315 aCDBCaching[4] = sizeof(aCachingModePage) & 0xff;
4316 aCDBCaching[5] = 0;
4317
4318 DataSeg.pvSeg = aCachingModePage;
4319 DataSeg.cbSeg = sizeof(aCachingModePage);
4320
4321 sr.enmXfer = SCSIXFER_TO_TARGET;
4322 sr.cbCDB = sizeof(aCDBCaching);
4323 sr.pvCDB = aCDBCaching;
4324 sr.cbI2TData = DataSeg.cbSeg;
4325 sr.paI2TSegs = &DataSeg;
4326 sr.cI2TSegs = 1;
4327 sr.cbT2IData = 0;
4328 sr.paT2ISegs = NULL;
4329 sr.cT2ISegs = 0;
4330 sr.cbSense = sizeof(sense);
4331 sr.pvSense = sense;
4332 sr.status = 0;
4333 rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
4334 if ( RT_SUCCESS(rc)
4335 && (sr.status == SCSI_STATUS_OK))
4336 {
4337 LogRel(("iSCSI: Enabled read and write cache of target %s\n", pImage->pszTargetName));
4338 }
4339 else
4340 {
4341 /* Log failures but continue. */
4342 LogRel(("iSCSI: Could not enable read and write cache of target %s, rc=%Rrc status=%#x\n",
4343 pImage->pszTargetName, rc, sr.status));
4344 LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sense));
4345 rc = VINF_SUCCESS;
4346 }
4347 }
4348 }
4349 else
4350 {
4351 /* Log errors but continue. */
4352 LogRel(("iSCSI: Could not check write cache of target %s, rc=%Rrc, got mode page %#x\n", pImage->pszTargetName, rc,aCachingModePage[0] & 0x3f));
4353 LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sense));
4354 rc = VINF_SUCCESS;
4355 }
4356
4357out:
4358 if (RT_FAILURE(rc))
4359 iscsiFreeImage(pImage, false);
4360 return rc;
4361}
4362
4363
4364/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
4365static int iscsiCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
4366 PVDINTERFACE pVDIfsImage)
4367{
4368 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
4369
4370 /* iSCSI images can't be checked for validity this way, as the filename
4371 * just can't supply enough configuration information. */
4372 int rc = VERR_VD_ISCSI_INVALID_HEADER;
4373
4374 LogFlowFunc(("returns %Rrc\n", rc));
4375 return rc;
4376}
4377
4378/** @copydoc VBOXHDDBACKEND::pfnOpen */
4379static int iscsiOpen(const char *pszFilename, unsigned uOpenFlags,
4380 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
4381 void **ppBackendData)
4382{
4383 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
4384 int rc;
4385 PISCSIIMAGE pImage;
4386
4387 /* Check open flags. All valid flags are supported. */
4388 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
4389 {
4390 rc = VERR_INVALID_PARAMETER;
4391 goto out;
4392 }
4393
4394 /* Check remaining arguments. */
4395 if ( !VALID_PTR(pszFilename)
4396 || !*pszFilename
4397 || strchr(pszFilename, '"'))
4398 {
4399 rc = VERR_INVALID_PARAMETER;
4400 goto out;
4401 }
4402
4403 pImage = (PISCSIIMAGE)RTMemAllocZ(sizeof(ISCSIIMAGE));
4404 if (!pImage)
4405 {
4406 rc = VERR_NO_MEMORY;
4407 goto out;
4408 }
4409
4410 pImage->pszFilename = pszFilename;
4411 pImage->pszInitiatorName = NULL;
4412 pImage->pszTargetName = NULL;
4413 pImage->pszTargetAddress = NULL;
4414 pImage->pszInitiatorUsername = NULL;
4415 pImage->pbInitiatorSecret = NULL;
4416 pImage->pszTargetUsername = NULL;
4417 pImage->pbTargetSecret = NULL;
4418 pImage->paCurrReq = NULL;
4419 pImage->pvRecvPDUBuf = NULL;
4420 pImage->pszHostname = NULL;
4421 pImage->pVDIfsDisk = pVDIfsDisk;
4422 pImage->pVDIfsImage = pVDIfsImage;
4423
4424 rc = iscsiOpenImage(pImage, uOpenFlags);
4425 if (RT_SUCCESS(rc))
4426 {
4427 LogFlowFunc(("target %s cVolume %d, cbSector %d\n", pImage->pszTargetName, pImage->cVolume, pImage->cbSector));
4428 LogRel(("iSCSI: target address %s, target name %s, SCSI LUN %lld\n", pImage->pszTargetAddress, pImage->pszTargetName, pImage->LUN));
4429 *ppBackendData = pImage;
4430 }
4431 else
4432 RTMemFree(pImage);
4433
4434out:
4435 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
4436 return rc;
4437}
4438
4439/** @copydoc VBOXHDDBACKEND::pfnCreate */
4440static int iscsiCreate(const char *pszFilename, uint64_t cbSize,
4441 unsigned uImageFlags, const char *pszComment,
4442 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
4443 PCRTUUID pUuid, unsigned uOpenFlags,
4444 unsigned uPercentStart, unsigned uPercentSpan,
4445 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
4446 PVDINTERFACE pVDIfsOperation, void **ppBackendData)
4447{
4448 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));
4449 int rc = VERR_NOT_SUPPORTED;
4450
4451 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
4452 return rc;
4453}
4454
4455/** @copydoc VBOXHDDBACKEND::pfnClose */
4456static int iscsiClose(void *pBackendData, bool fDelete)
4457{
4458 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
4459 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4460 int rc;
4461
4462 Assert(!fDelete); /* This flag is unsupported. */
4463
4464 rc = iscsiFreeImage(pImage, fDelete);
4465 RTMemFree(pImage);
4466
4467 LogFlowFunc(("returns %Rrc\n", rc));
4468 return rc;
4469}
4470
4471/** @copydoc VBOXHDDBACKEND::pfnRead */
4472static int iscsiRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
4473 size_t cbToRead, size_t *pcbActuallyRead)
4474{
4475 /** @todo reinstate logging of the target everywhere - dropped temporarily */
4476 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
4477 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4478 uint64_t lba;
4479 uint16_t tls;
4480 int rc;
4481
4482 Assert(pImage);
4483 Assert(uOffset % 512 == 0);
4484 Assert(cbToRead % 512 == 0);
4485
4486 Assert(pImage->cbSector);
4487 AssertPtr(pvBuf);
4488
4489 if ( uOffset + cbToRead > pImage->cbSize
4490 || cbToRead == 0)
4491 {
4492 rc = VERR_INVALID_PARAMETER;
4493 goto out;
4494 }
4495
4496 /*
4497 * Clip read size to a value which is supported by the target.
4498 */
4499 cbToRead = RT_MIN(cbToRead, pImage->cbRecvDataLength);
4500
4501 lba = uOffset / pImage->cbSector;
4502 tls = (uint16_t)(cbToRead / pImage->cbSector);
4503 SCSIREQ sr;
4504 RTSGSEG T2ISeg;
4505 size_t cbCDB;
4506 uint8_t abCDB[16];
4507 uint8_t sense[96];
4508
4509 if (pImage->cVolume < _4G)
4510 {
4511 cbCDB = 10;
4512 abCDB[0] = SCSI_READ_10;
4513 abCDB[1] = 0; /* reserved */
4514 abCDB[2] = (lba >> 24) & 0xff;
4515 abCDB[3] = (lba >> 16) & 0xff;
4516 abCDB[4] = (lba >> 8) & 0xff;
4517 abCDB[5] = lba & 0xff;
4518 abCDB[6] = 0; /* reserved */
4519 abCDB[7] = (tls >> 8) & 0xff;
4520 abCDB[8] = tls & 0xff;
4521 abCDB[9] = 0; /* control */
4522 }
4523 else
4524 {
4525 cbCDB = 16;
4526 abCDB[0] = SCSI_READ_16;
4527 abCDB[1] = 0; /* reserved */
4528 abCDB[2] = (lba >> 56) & 0xff;
4529 abCDB[3] = (lba >> 48) & 0xff;
4530 abCDB[4] = (lba >> 40) & 0xff;
4531 abCDB[5] = (lba >> 32) & 0xff;
4532 abCDB[6] = (lba >> 24) & 0xff;
4533 abCDB[7] = (lba >> 16) & 0xff;
4534 abCDB[8] = (lba >> 8) & 0xff;
4535 abCDB[9] = lba & 0xff;
4536 abCDB[10] = 0; /* tls unused */
4537 abCDB[11] = 0; /* tls unused */
4538 abCDB[12] = (tls >> 8) & 0xff;
4539 abCDB[13] = tls & 0xff;
4540 abCDB[14] = 0; /* reserved */
4541 abCDB[15] = 0; /* reserved */
4542 }
4543
4544 T2ISeg.pvSeg = pvBuf;
4545 T2ISeg.cbSeg = cbToRead;
4546
4547 sr.enmXfer = SCSIXFER_FROM_TARGET;
4548 sr.cbCDB = cbCDB;
4549 sr.pvCDB = abCDB;
4550 sr.cbI2TData = 0;
4551 sr.paI2TSegs = NULL;
4552 sr.cI2TSegs = 0;
4553 sr.cbT2IData = cbToRead;
4554 sr.paT2ISegs = &T2ISeg;
4555 sr.cT2ISegs = 1;
4556 sr.cbSense = sizeof(sense);
4557 sr.pvSense = sense;
4558
4559 rc = iscsiCommandSync(pImage, &sr, true, VERR_READ_ERROR);
4560 if (RT_FAILURE(rc))
4561 {
4562 AssertMsgFailed(("iscsiCommand(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
4563 *pcbActuallyRead = 0;
4564 }
4565 else
4566 *pcbActuallyRead = sr.cbT2IData;
4567
4568out:
4569 LogFlowFunc(("returns %Rrc\n", rc));
4570 return rc;
4571}
4572
4573/** @copydoc VBOXHDDBACKEND::pfnWrite */
4574static int iscsiWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
4575 size_t cbToWrite, size_t *pcbWriteProcess,
4576 size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
4577{
4578 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
4579 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4580 uint64_t lba;
4581 uint16_t tls;
4582 int rc;
4583
4584 Assert(pImage);
4585 Assert(uOffset % 512 == 0);
4586 Assert(cbToWrite % 512 == 0);
4587
4588 Assert(pImage->cbSector);
4589 Assert(pvBuf);
4590
4591 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
4592 {
4593 rc = VERR_VD_IMAGE_READ_ONLY;
4594 goto out;
4595 }
4596
4597 *pcbPreRead = 0;
4598 *pcbPostRead = 0;
4599
4600 /*
4601 * Clip write size to a value which is supported by the target.
4602 */
4603 cbToWrite = RT_MIN(cbToWrite, pImage->cbSendDataLength);
4604
4605 lba = uOffset / pImage->cbSector;
4606 tls = (uint16_t)(cbToWrite / pImage->cbSector);
4607 SCSIREQ sr;
4608 RTSGSEG I2TSeg;
4609 size_t cbCDB;
4610 uint8_t abCDB[16];
4611 uint8_t sense[96];
4612
4613 if (pImage->cVolume < _4G)
4614 {
4615 cbCDB = 10;
4616 abCDB[0] = SCSI_WRITE_10;
4617 abCDB[1] = 0; /* reserved */
4618 abCDB[2] = (lba >> 24) & 0xff;
4619 abCDB[3] = (lba >> 16) & 0xff;
4620 abCDB[4] = (lba >> 8) & 0xff;
4621 abCDB[5] = lba & 0xff;
4622 abCDB[6] = 0; /* reserved */
4623 abCDB[7] = (tls >> 8) & 0xff;
4624 abCDB[8] = tls & 0xff;
4625 abCDB[9] = 0; /* control */
4626 }
4627 else
4628 {
4629 cbCDB = 16;
4630 abCDB[0] = SCSI_WRITE_16;
4631 abCDB[1] = 0; /* reserved */
4632 abCDB[2] = (lba >> 56) & 0xff;
4633 abCDB[3] = (lba >> 48) & 0xff;
4634 abCDB[4] = (lba >> 40) & 0xff;
4635 abCDB[5] = (lba >> 32) & 0xff;
4636 abCDB[6] = (lba >> 24) & 0xff;
4637 abCDB[7] = (lba >> 16) & 0xff;
4638 abCDB[8] = (lba >> 8) & 0xff;
4639 abCDB[9] = lba & 0xff;
4640 abCDB[10] = 0; /* tls unused */
4641 abCDB[11] = 0; /* tls unused */
4642 abCDB[12] = (tls >> 8) & 0xff;
4643 abCDB[13] = tls & 0xff;
4644 abCDB[14] = 0; /* reserved */
4645 abCDB[15] = 0; /* reserved */
4646 }
4647
4648 I2TSeg.pvSeg = (void *)pvBuf;
4649 I2TSeg.cbSeg = cbToWrite;
4650
4651 sr.enmXfer = SCSIXFER_TO_TARGET;
4652 sr.cbCDB = cbCDB;
4653 sr.pvCDB = abCDB;
4654 sr.cbI2TData = cbToWrite;
4655 sr.paI2TSegs = &I2TSeg;
4656 sr.cI2TSegs = 1;
4657 sr.cbT2IData = 0;
4658 sr.paT2ISegs = NULL;
4659 sr.cT2ISegs = 0;
4660 sr.cbSense = sizeof(sense);
4661 sr.pvSense = sense;
4662
4663 rc = iscsiCommandSync(pImage, &sr, true, VERR_WRITE_ERROR);
4664 if (RT_FAILURE(rc))
4665 {
4666 AssertMsgFailed(("iscsiCommand(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
4667 *pcbWriteProcess = 0;
4668 }
4669 else
4670 *pcbWriteProcess = cbToWrite;
4671
4672out:
4673 LogFlowFunc(("returns %Rrc\n", rc));
4674 return rc;
4675}
4676
4677/** @copydoc VBOXHDDBACKEND::pfnFlush */
4678static int iscsiFlush(void *pBackendData)
4679{
4680 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
4681 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4682 int rc;
4683
4684 Assert(pImage);
4685
4686 SCSIREQ sr;
4687 uint8_t abCDB[10];
4688 uint8_t sense[96];
4689
4690 abCDB[0] = SCSI_SYNCHRONIZE_CACHE;
4691 abCDB[1] = 0; /* reserved */
4692 abCDB[2] = 0; /* LBA 0 */
4693 abCDB[3] = 0; /* LBA 0 */
4694 abCDB[4] = 0; /* LBA 0 */
4695 abCDB[5] = 0; /* LBA 0 */
4696 abCDB[6] = 0; /* reserved */
4697 abCDB[7] = 0; /* transfer everything to disk */
4698 abCDB[8] = 0; /* transfer everything to disk */
4699 abCDB[9] = 0; /* control */
4700
4701 sr.enmXfer = SCSIXFER_NONE;
4702 sr.cbCDB = sizeof(abCDB);
4703 sr.pvCDB = abCDB;
4704 sr.cbI2TData = 0;
4705 sr.paI2TSegs = NULL;
4706 sr.cI2TSegs = 0;
4707 sr.cbT2IData = 0;
4708 sr.paT2ISegs = NULL;
4709 sr.cT2ISegs = 0;
4710 sr.cbSense = sizeof(sense);
4711 sr.pvSense = sense;
4712
4713 rc = iscsiCommandSync(pImage, &sr, false, VINF_SUCCESS);
4714 if (RT_FAILURE(rc))
4715 AssertMsgFailed(("iscsiCommand(%s) -> %Rrc\n", pImage->pszTargetName, rc));
4716 LogFlowFunc(("returns %Rrc\n", rc));
4717 return rc;
4718}
4719
4720/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
4721static unsigned iscsiGetVersion(void *pBackendData)
4722{
4723 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
4724 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4725
4726 Assert(pImage);
4727 NOREF(pImage);
4728
4729 return 0;
4730}
4731
4732/** @copydoc VBOXHDDBACKEND::pfnGetSize */
4733static uint64_t iscsiGetSize(void *pBackendData)
4734{
4735 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
4736 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4737
4738 Assert(pImage);
4739
4740 if (pImage)
4741 return pImage->cbSize;
4742 else
4743 return 0;
4744}
4745
4746/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
4747static uint64_t iscsiGetFileSize(void *pBackendData)
4748{
4749 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
4750 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4751
4752 Assert(pImage);
4753 NOREF(pImage);
4754
4755 if (pImage)
4756 return pImage->cbSize;
4757 else
4758 return 0;
4759}
4760
4761/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
4762static int iscsiGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
4763{
4764 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
4765 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4766 int rc;
4767
4768 Assert(pImage);
4769
4770 if (pImage)
4771 rc = VERR_VD_GEOMETRY_NOT_SET;
4772 else
4773 rc = VERR_VD_NOT_OPENED;
4774
4775 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
4776 return rc;
4777}
4778
4779/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
4780static int iscsiSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
4781{
4782 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
4783 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4784 int rc;
4785
4786 Assert(pImage);
4787
4788 if (pImage)
4789 {
4790 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
4791 {
4792 rc = VERR_VD_IMAGE_READ_ONLY;
4793 goto out;
4794 }
4795 rc = VERR_VD_GEOMETRY_NOT_SET;
4796 }
4797 else
4798 rc = VERR_VD_NOT_OPENED;
4799
4800out:
4801 LogFlowFunc(("returns %Rrc\n", rc));
4802 return rc;
4803}
4804
4805/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
4806static int iscsiGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
4807{
4808 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
4809 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4810 int rc;
4811
4812 Assert(pImage);
4813
4814 if (pImage)
4815 rc = VERR_VD_GEOMETRY_NOT_SET;
4816 else
4817 rc = VERR_VD_NOT_OPENED;
4818
4819 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
4820 return rc;
4821}
4822
4823/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
4824static int iscsiSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
4825{
4826 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
4827 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4828 int rc;
4829
4830 Assert(pImage);
4831
4832 if (pImage)
4833 {
4834 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
4835 {
4836 rc = VERR_VD_IMAGE_READ_ONLY;
4837 goto out;
4838 }
4839 rc = VERR_VD_GEOMETRY_NOT_SET;
4840 }
4841 else
4842 rc = VERR_VD_NOT_OPENED;
4843
4844out:
4845 LogFlowFunc(("returns %Rrc\n", rc));
4846 return rc;
4847}
4848
4849/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
4850static unsigned iscsiGetImageFlags(void *pBackendData)
4851{
4852 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
4853 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4854 unsigned uImageFlags;
4855
4856 Assert(pImage);
4857 NOREF(pImage);
4858
4859 uImageFlags = VD_IMAGE_FLAGS_FIXED;
4860
4861 LogFlowFunc(("returns %#x\n", uImageFlags));
4862 return uImageFlags;
4863}
4864
4865/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
4866static unsigned iscsiGetOpenFlags(void *pBackendData)
4867{
4868 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
4869 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4870 unsigned uOpenFlags;
4871
4872 Assert(pImage);
4873
4874 if (pImage)
4875 uOpenFlags = pImage->uOpenFlags;
4876 else
4877 uOpenFlags = 0;
4878
4879 LogFlowFunc(("returns %#x\n", uOpenFlags));
4880 return uOpenFlags;
4881}
4882
4883/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
4884static int iscsiSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
4885{
4886 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
4887 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4888 int rc;
4889
4890 /* Image must be opened and the new flags must be valid. Just readonly and
4891 * info flags are supported. */
4892 if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE)))
4893 {
4894 rc = VERR_INVALID_PARAMETER;
4895 goto out;
4896 }
4897
4898 /* Implement this operation via reopening the image if we actually need
4899 * to do something. A read/write -> readonly transition doesn't need a
4900 * reopen. In the other direction we don't have the necessary information
4901 * as the "disk is readonly" flag is thrown away. Can be optimized too,
4902 * but it's not worth the effort at the moment. */
4903 if ( !(uOpenFlags & VD_OPEN_FLAGS_READONLY)
4904 && (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
4905 {
4906 iscsiFreeImage(pImage, false);
4907 rc = iscsiOpenImage(pImage, uOpenFlags);
4908 }
4909 else
4910 {
4911 pImage->uOpenFlags = uOpenFlags;
4912 rc = VINF_SUCCESS;
4913 }
4914out:
4915 LogFlowFunc(("returns %Rrc\n", rc));
4916 return rc;
4917}
4918
4919/** @copydoc VBOXHDDBACKEND::pfnGetComment */
4920static int iscsiGetComment(void *pBackendData, char *pszComment,
4921 size_t cbComment)
4922{
4923 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
4924 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4925 int rc;
4926
4927 Assert(pImage);
4928
4929 if (pImage)
4930 rc = VERR_NOT_SUPPORTED;
4931 else
4932 rc = VERR_VD_NOT_OPENED;
4933
4934 LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
4935 return rc;
4936}
4937
4938/** @copydoc VBOXHDDBACKEND::pfnSetComment */
4939static int iscsiSetComment(void *pBackendData, const char *pszComment)
4940{
4941 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
4942 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4943 int rc;
4944
4945 Assert(pImage);
4946
4947 if (pImage)
4948 {
4949 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
4950 rc = VERR_NOT_SUPPORTED;
4951 else
4952 rc = VERR_VD_IMAGE_READ_ONLY;
4953 }
4954 else
4955 rc = VERR_VD_NOT_OPENED;
4956
4957 LogFlowFunc(("returns %Rrc\n", rc));
4958 return rc;
4959}
4960
4961/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
4962static int iscsiGetUuid(void *pBackendData, PRTUUID pUuid)
4963{
4964 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
4965 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4966 int rc;
4967
4968 Assert(pImage);
4969
4970 if (pImage)
4971 rc = VERR_NOT_SUPPORTED;
4972 else
4973 rc = VERR_VD_NOT_OPENED;
4974
4975 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
4976 return rc;
4977}
4978
4979/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
4980static int iscsiSetUuid(void *pBackendData, PCRTUUID pUuid)
4981{
4982 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
4983 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
4984 int rc;
4985
4986 LogFlowFunc(("%RTuuid\n", pUuid));
4987 Assert(pImage);
4988
4989 if (pImage)
4990 {
4991 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
4992 rc = VERR_NOT_SUPPORTED;
4993 else
4994 rc = VERR_VD_IMAGE_READ_ONLY;
4995 }
4996 else
4997 rc = VERR_VD_NOT_OPENED;
4998
4999 LogFlowFunc(("returns %Rrc\n", rc));
5000 return rc;
5001}
5002
5003/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
5004static int iscsiGetModificationUuid(void *pBackendData, PRTUUID pUuid)
5005{
5006 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
5007 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5008 int rc;
5009
5010 Assert(pImage);
5011
5012 if (pImage)
5013 rc = VERR_NOT_SUPPORTED;
5014 else
5015 rc = VERR_VD_NOT_OPENED;
5016
5017 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
5018 return rc;
5019}
5020
5021/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
5022static int iscsiSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
5023{
5024 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
5025 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5026 int rc;
5027
5028 LogFlowFunc(("%RTuuid\n", pUuid));
5029 Assert(pImage);
5030
5031 if (pImage)
5032 {
5033 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
5034 rc = VERR_NOT_SUPPORTED;
5035 else
5036 rc = VERR_VD_IMAGE_READ_ONLY;
5037 }
5038 else
5039 rc = VERR_VD_NOT_OPENED;
5040
5041 LogFlowFunc(("returns %Rrc\n", rc));
5042 return rc;
5043}
5044
5045/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
5046static int iscsiGetParentUuid(void *pBackendData, PRTUUID pUuid)
5047{
5048 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
5049 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5050 int rc;
5051
5052 Assert(pImage);
5053
5054 if (pImage)
5055 rc = VERR_NOT_SUPPORTED;
5056 else
5057 rc = VERR_VD_NOT_OPENED;
5058
5059 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
5060 return rc;
5061}
5062
5063/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
5064static int iscsiSetParentUuid(void *pBackendData, PCRTUUID pUuid)
5065{
5066 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
5067 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5068 int rc;
5069
5070 LogFlowFunc(("%RTuuid\n", pUuid));
5071 Assert(pImage);
5072
5073 if (pImage)
5074 {
5075 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
5076 rc = VERR_NOT_SUPPORTED;
5077 else
5078 rc = VERR_VD_IMAGE_READ_ONLY;
5079 }
5080 else
5081 rc = VERR_VD_NOT_OPENED;
5082
5083 LogFlowFunc(("returns %Rrc\n", rc));
5084 return rc;
5085}
5086
5087/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
5088static int iscsiGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
5089{
5090 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
5091 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5092 int rc;
5093
5094 Assert(pImage);
5095
5096 if (pImage)
5097 rc = VERR_NOT_SUPPORTED;
5098 else
5099 rc = VERR_VD_NOT_OPENED;
5100
5101 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
5102 return rc;
5103}
5104
5105/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
5106static int iscsiSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
5107{
5108 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
5109 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5110 int rc;
5111
5112 LogFlowFunc(("%RTuuid\n", pUuid));
5113 Assert(pImage);
5114
5115 if (pImage)
5116 {
5117 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
5118 rc = VERR_NOT_SUPPORTED;
5119 else
5120 rc = VERR_VD_IMAGE_READ_ONLY;
5121 }
5122 else
5123 rc = VERR_VD_NOT_OPENED;
5124
5125 LogFlowFunc(("returns %Rrc\n", rc));
5126 return rc;
5127}
5128
5129/** @copydoc VBOXHDDBACKEND::pfnDump */
5130static void iscsiDump(void *pBackendData)
5131{
5132 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5133
5134 Assert(pImage);
5135 if (pImage)
5136 {
5137 /** @todo put something useful here */
5138 iscsiMessage(pImage, "Header: cVolume=%u\n", pImage->cVolume);
5139 }
5140}
5141
5142/** @copydoc VBOXHDDBACKEND::pfnIsAsyncIOSupported */
5143static bool iscsiIsAsyncIOSupported(void *pBackendData)
5144{
5145 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5146 return pImage->fCmdQueuingSupported;
5147}
5148
5149/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
5150static int iscsiAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
5151 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
5152{
5153 LogFlowFunc(("pBackendData=%p uOffset=%#llx pIoCtx=%#p cbToRead=%u pcbActuallyRead=%p\n",
5154 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
5155 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5156 int rc = VINF_SUCCESS;
5157
5158 if (uOffset + cbToRead > pImage->cbSize)
5159 return VERR_INVALID_PARAMETER;
5160
5161 /*
5162 * Clip read size to a value which is supported by the target.
5163 */
5164 cbToRead = RT_MIN(cbToRead, pImage->cbRecvDataLength);
5165
5166 unsigned cT2ISegs = 0;
5167 size_t cbSegs = 0;
5168
5169 /* Get the number of segments. */
5170 cbSegs = pImage->pInterfaceIoCallbacks->pfnIoCtxSegArrayCreate(pImage->pInterfaceIo->pvUser, pIoCtx,
5171 NULL, &cT2ISegs, cbToRead);
5172 Assert(cbSegs == cbToRead);
5173
5174 PSCSIREQASYNC pReqAsync = (PSCSIREQASYNC)RTMemAllocZ(RT_OFFSETOF(SCSIREQASYNC, aSegs[cT2ISegs]));
5175 if (RT_LIKELY(pReqAsync))
5176 {
5177 PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(sizeof(SCSIREQ));
5178 if (pReq)
5179 {
5180 uint64_t lba;
5181 uint16_t tls;
5182 uint8_t *pbCDB = &pReqAsync->abCDB[0];
5183 size_t cbCDB;
5184
5185 lba = uOffset / pImage->cbSector;
5186 tls = (uint16_t)(cbToRead / pImage->cbSector);
5187
5188 cbSegs = pImage->pInterfaceIoCallbacks->pfnIoCtxSegArrayCreate(pImage->pInterfaceIo->pvUser, pIoCtx,
5189 &pReqAsync->aSegs[0],
5190 &cT2ISegs, cbToRead);
5191 Assert(cbSegs == cbToRead);
5192 pReqAsync->cT2ISegs = cT2ISegs;
5193 pReqAsync->pIoCtx = pIoCtx;
5194 pReqAsync->pScsiReq = pReq;
5195 pReqAsync->cSenseRetries = 10;
5196 pReqAsync->rcSense = VERR_READ_ERROR;
5197
5198 if (pImage->cVolume < _4G)
5199 {
5200 cbCDB = 10;
5201 pbCDB[0] = SCSI_READ_10;
5202 pbCDB[1] = 0; /* reserved */
5203 pbCDB[2] = (lba >> 24) & 0xff;
5204 pbCDB[3] = (lba >> 16) & 0xff;
5205 pbCDB[4] = (lba >> 8) & 0xff;
5206 pbCDB[5] = lba & 0xff;
5207 pbCDB[6] = 0; /* reserved */
5208 pbCDB[7] = (tls >> 8) & 0xff;
5209 pbCDB[8] = tls & 0xff;
5210 pbCDB[9] = 0; /* control */
5211 }
5212 else
5213 {
5214 cbCDB = 16;
5215 pbCDB[0] = SCSI_READ_16;
5216 pbCDB[1] = 0; /* reserved */
5217 pbCDB[2] = (lba >> 56) & 0xff;
5218 pbCDB[3] = (lba >> 48) & 0xff;
5219 pbCDB[4] = (lba >> 40) & 0xff;
5220 pbCDB[5] = (lba >> 32) & 0xff;
5221 pbCDB[6] = (lba >> 24) & 0xff;
5222 pbCDB[7] = (lba >> 16) & 0xff;
5223 pbCDB[8] = (lba >> 8) & 0xff;
5224 pbCDB[9] = lba & 0xff;
5225 pbCDB[10] = 0; /* tls unused */
5226 pbCDB[11] = 0; /* tls unused */
5227 pbCDB[12] = (tls >> 8) & 0xff;
5228 pbCDB[13] = tls & 0xff;
5229 pbCDB[14] = 0; /* reserved */
5230 pbCDB[15] = 0; /* reserved */
5231 }
5232
5233 pReq->enmXfer = SCSIXFER_FROM_TARGET;
5234 pReq->cbCDB = cbCDB;
5235 pReq->pvCDB = pReqAsync->abCDB;
5236 pReq->cbI2TData = 0;
5237 pReq->paI2TSegs = NULL;
5238 pReq->cI2TSegs = 0;
5239 pReq->cbT2IData = cbToRead;
5240 pReq->paT2ISegs = &pReqAsync->aSegs[pReqAsync->cI2TSegs];
5241 pReq->cT2ISegs = pReqAsync->cT2ISegs;
5242 pReq->cbSense = sizeof(pReqAsync->abSense);
5243 pReq->pvSense = pReqAsync->abSense;
5244
5245 rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReqAsync);
5246 if (RT_FAILURE(rc))
5247 AssertMsgFailed(("iscsiCommand(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
5248 else
5249 {
5250 *pcbActuallyRead = cbToRead;
5251 return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
5252 }
5253
5254 RTMemFree(pReq);
5255 }
5256 else
5257 rc = VERR_NO_MEMORY;
5258
5259 RTMemFree(pReqAsync);
5260 }
5261 else
5262 rc = VERR_NO_MEMORY;
5263
5264 LogFlowFunc(("returns rc=%Rrc\n", rc));
5265 return rc;
5266}
5267
5268/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
5269static int iscsiAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
5270 PVDIOCTX pIoCtx,
5271 size_t *pcbWriteProcess, size_t *pcbPreRead,
5272 size_t *pcbPostRead, unsigned fWrite)
5273{
5274 LogFlowFunc(("pBackendData=%p uOffset=%llu pIoCtx=%#p cbToWrite=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
5275 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
5276 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5277 int rc = VINF_SUCCESS;
5278
5279 AssertPtr(pImage);
5280 Assert(uOffset % 512 == 0);
5281 Assert(cbToWrite % 512 == 0);
5282
5283 if (uOffset + cbToWrite > pImage->cbSize)
5284 return VERR_INVALID_PARAMETER;
5285
5286 /*
5287 * Clip read size to a value which is supported by the target.
5288 */
5289 cbToWrite = RT_MIN(cbToWrite, pImage->cbSendDataLength);
5290
5291 unsigned cI2TSegs = 0;
5292 size_t cbSegs = 0;
5293
5294 /* Get the number of segments. */
5295 cbSegs = pImage->pInterfaceIoCallbacks->pfnIoCtxSegArrayCreate(pImage->pInterfaceIo->pvUser, pIoCtx,
5296 NULL, &cI2TSegs, cbToWrite);
5297 Assert(cbSegs == cbToWrite);
5298
5299 PSCSIREQASYNC pReqAsync = (PSCSIREQASYNC)RTMemAllocZ(RT_OFFSETOF(SCSIREQASYNC, aSegs[cI2TSegs]));
5300 if (RT_LIKELY(pReqAsync))
5301 {
5302 PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(sizeof(SCSIREQ));
5303 if (pReq)
5304 {
5305 uint64_t lba;
5306 uint16_t tls;
5307 uint8_t *pbCDB = &pReqAsync->abCDB[0];
5308 size_t cbCDB;
5309
5310 lba = uOffset / pImage->cbSector;
5311 tls = (uint16_t)(cbToWrite / pImage->cbSector);
5312
5313 cbSegs = pImage->pInterfaceIoCallbacks->pfnIoCtxSegArrayCreate(pImage->pInterfaceIo->pvUser, pIoCtx,
5314 &pReqAsync->aSegs[0],
5315 &cI2TSegs, cbToWrite);
5316 Assert(cbSegs == cbToWrite);
5317 pReqAsync->cI2TSegs = cI2TSegs;
5318 pReqAsync->pIoCtx = pIoCtx;
5319 pReqAsync->pScsiReq = pReq;
5320 pReqAsync->cSenseRetries = 10;
5321 pReqAsync->rcSense = VERR_WRITE_ERROR;
5322
5323 if (pImage->cVolume < _4G)
5324 {
5325 cbCDB = 10;
5326 pbCDB[0] = SCSI_WRITE_10;
5327 pbCDB[1] = 0; /* reserved */
5328 pbCDB[2] = (lba >> 24) & 0xff;
5329 pbCDB[3] = (lba >> 16) & 0xff;
5330 pbCDB[4] = (lba >> 8) & 0xff;
5331 pbCDB[5] = lba & 0xff;
5332 pbCDB[6] = 0; /* reserved */
5333 pbCDB[7] = (tls >> 8) & 0xff;
5334 pbCDB[8] = tls & 0xff;
5335 pbCDB[9] = 0; /* control */
5336 }
5337 else
5338 {
5339 cbCDB = 16;
5340 pbCDB[0] = SCSI_WRITE_16;
5341 pbCDB[1] = 0; /* reserved */
5342 pbCDB[2] = (lba >> 56) & 0xff;
5343 pbCDB[3] = (lba >> 48) & 0xff;
5344 pbCDB[4] = (lba >> 40) & 0xff;
5345 pbCDB[5] = (lba >> 32) & 0xff;
5346 pbCDB[6] = (lba >> 24) & 0xff;
5347 pbCDB[7] = (lba >> 16) & 0xff;
5348 pbCDB[8] = (lba >> 8) & 0xff;
5349 pbCDB[9] = lba & 0xff;
5350 pbCDB[10] = 0; /* tls unused */
5351 pbCDB[11] = 0; /* tls unused */
5352 pbCDB[12] = (tls >> 8) & 0xff;
5353 pbCDB[13] = tls & 0xff;
5354 pbCDB[14] = 0; /* reserved */
5355 pbCDB[15] = 0; /* reserved */
5356 }
5357
5358 pReq->enmXfer = SCSIXFER_TO_TARGET;
5359 pReq->cbCDB = cbCDB;
5360 pReq->pvCDB = pReqAsync->abCDB;
5361 pReq->cbI2TData = cbToWrite;
5362 pReq->paI2TSegs = &pReqAsync->aSegs[0];
5363 pReq->cI2TSegs = pReqAsync->cI2TSegs;
5364 pReq->cbT2IData = 0;
5365 pReq->paT2ISegs = NULL;
5366 pReq->cT2ISegs = 0;
5367 pReq->cbSense = sizeof(pReqAsync->abSense);
5368 pReq->pvSense = pReqAsync->abSense;
5369
5370 rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReqAsync);
5371 if (RT_FAILURE(rc))
5372 AssertMsgFailed(("iscsiCommand(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
5373 else
5374 {
5375 *pcbWriteProcess = cbToWrite;
5376 return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
5377 }
5378
5379 RTMemFree(pReq);
5380 }
5381 else
5382 rc = VERR_NO_MEMORY;
5383
5384 RTMemFree(pReqAsync);
5385 }
5386 else
5387 rc = VERR_NO_MEMORY;
5388
5389 LogFlowFunc(("returns rc=%Rrc\n", rc));
5390 return rc;
5391}
5392
5393/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
5394static int iscsiAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
5395{
5396 LogFlowFunc(("pBackendData=%p pIoCtx=%#p\n", pBackendData, pIoCtx));
5397 PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
5398 int rc = VINF_SUCCESS;
5399
5400 PSCSIREQASYNC pReqAsync = (PSCSIREQASYNC)RTMemAllocZ(sizeof(SCSIREQASYNC));
5401 if (RT_LIKELY(pReqAsync))
5402 {
5403 PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(sizeof(SCSIREQ));
5404 if (pReq)
5405 {
5406 uint8_t *pbCDB = &pReqAsync->abCDB[0];
5407
5408 pReqAsync->pIoCtx = pIoCtx;
5409 pReqAsync->pScsiReq = pReq;
5410 pReqAsync->cSenseRetries = 0;
5411 pReqAsync->rcSense = VINF_SUCCESS;
5412
5413 pbCDB[0] = SCSI_SYNCHRONIZE_CACHE;
5414 pbCDB[1] = 0; /* reserved */
5415 pbCDB[2] = 0; /* reserved */
5416 pbCDB[3] = 0; /* reserved */
5417 pbCDB[4] = 0; /* reserved */
5418 pbCDB[5] = 0; /* reserved */
5419 pbCDB[6] = 0; /* reserved */
5420 pbCDB[7] = 0; /* reserved */
5421 pbCDB[8] = 0; /* reserved */
5422 pbCDB[9] = 0; /* control */
5423
5424 pReq->enmXfer = SCSIXFER_NONE;
5425 pReq->cbCDB = 10;
5426 pReq->pvCDB = pReqAsync->abCDB;
5427 pReq->cbI2TData = 0;
5428 pReq->paI2TSegs = NULL;
5429 pReq->cI2TSegs = 0;
5430 pReq->cbT2IData = 0;
5431 pReq->paT2ISegs = NULL;
5432 pReq->cT2ISegs = 0;
5433 pReq->cbSense = sizeof(pReqAsync->abSense);
5434 pReq->pvSense = pReqAsync->abSense;
5435
5436 rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReqAsync);
5437 if (RT_FAILURE(rc))
5438 AssertMsgFailed(("iscsiCommand(%s) -> %Rrc\n", pImage->pszTargetName, rc));
5439 else
5440 return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
5441
5442 RTMemFree(pReq);
5443 }
5444 else
5445 rc = VERR_NO_MEMORY;
5446
5447 RTMemFree(pReqAsync);
5448 }
5449 else
5450 rc = VERR_NO_MEMORY;
5451
5452 LogFlowFunc(("returns rc=%Rrc\n", rc));
5453 return rc;
5454}
5455
5456/** @copydoc VBOXHDDBACKEND::pfnComposeLocation */
5457static int iscsiComposeLocation(PVDINTERFACE pConfig, char **pszLocation)
5458{
5459 char *pszTarget = NULL;
5460 char *pszLUN = NULL;
5461 char *pszAddress = NULL;
5462 int rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetName", &pszTarget);
5463 if (RT_SUCCESS(rc))
5464 {
5465 rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "LUN", &pszLUN);
5466 if (RT_SUCCESS(rc))
5467 {
5468 rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetAddress", &pszAddress);
5469 if (RT_SUCCESS(rc))
5470 {
5471 if (RTStrAPrintf(pszLocation, "iscsi://%s/%s/%s",
5472 pszAddress, pszTarget, pszLUN) < 0)
5473 rc = VERR_NO_MEMORY;
5474 }
5475 }
5476 }
5477 RTMemFree(pszTarget);
5478 RTMemFree(pszLUN);
5479 RTMemFree(pszAddress);
5480 return rc;
5481}
5482
5483/** @copydoc VBOXHDDBACKEND::pfnComposeName */
5484static int iscsiComposeName(PVDINTERFACE pConfig, char **pszName)
5485{
5486 char *pszTarget = NULL;
5487 char *pszLUN = NULL;
5488 char *pszAddress = NULL;
5489 int rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetName", &pszTarget);
5490 if (RT_SUCCESS(rc))
5491 {
5492 rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "LUN", &pszLUN);
5493 if (RT_SUCCESS(rc))
5494 {
5495 rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetAddress", &pszAddress);
5496 if (RT_SUCCESS(rc))
5497 {
5498 /** @todo think about a nicer looking location scheme for iSCSI */
5499 if (RTStrAPrintf(pszName, "%s/%s/%s",
5500 pszAddress, pszTarget, pszLUN) < 0)
5501 rc = VERR_NO_MEMORY;
5502 }
5503 }
5504 }
5505 RTMemFree(pszTarget);
5506 RTMemFree(pszLUN);
5507 RTMemFree(pszAddress);
5508
5509 return rc;
5510}
5511
5512
5513VBOXHDDBACKEND g_ISCSIBackend =
5514{
5515 /* pszBackendName */
5516 "iSCSI",
5517 /* cbSize */
5518 sizeof(VBOXHDDBACKEND),
5519 /* uBackendCaps */
5520 VD_CAP_CONFIG | VD_CAP_TCPNET | VD_CAP_ASYNC,
5521 /* papszFileExtensions */
5522 NULL,
5523 /* paConfigInfo */
5524 s_iscsiConfigInfo,
5525 /* hPlugin */
5526 NIL_RTLDRMOD,
5527 /* pfnCheckIfValid */
5528 iscsiCheckIfValid,
5529 /* pfnOpen */
5530 iscsiOpen,
5531 /* pfnCreate */
5532 iscsiCreate,
5533 /* pfnRename */
5534 NULL,
5535 /* pfnClose */
5536 iscsiClose,
5537 /* pfnRead */
5538 iscsiRead,
5539 /* pfnWrite */
5540 iscsiWrite,
5541 /* pfnFlush */
5542 iscsiFlush,
5543 /* pfnGetVersion */
5544 iscsiGetVersion,
5545 /* pfnGetSize */
5546 iscsiGetSize,
5547 /* pfnGetFileSize */
5548 iscsiGetFileSize,
5549 /* pfnGetPCHSGeometry */
5550 iscsiGetPCHSGeometry,
5551 /* pfnSetPCHSGeometry */
5552 iscsiSetPCHSGeometry,
5553 /* pfnGetLCHSGeometry */
5554 iscsiGetLCHSGeometry,
5555 /* pfnSetLCHSGeometry */
5556 iscsiSetLCHSGeometry,
5557 /* pfnGetImageFlags */
5558 iscsiGetImageFlags,
5559 /* pfnGetOpenFlags */
5560 iscsiGetOpenFlags,
5561 /* pfnSetOpenFlags */
5562 iscsiSetOpenFlags,
5563 /* pfnGetComment */
5564 iscsiGetComment,
5565 /* pfnSetComment */
5566 iscsiSetComment,
5567 /* pfnGetUuid */
5568 iscsiGetUuid,
5569 /* pfnSetUuid */
5570 iscsiSetUuid,
5571 /* pfnGetModificationUuid */
5572 iscsiGetModificationUuid,
5573 /* pfnSetModificationUuid */
5574 iscsiSetModificationUuid,
5575 /* pfnGetParentUuid */
5576 iscsiGetParentUuid,
5577 /* pfnSetParentUuid */
5578 iscsiSetParentUuid,
5579 /* pfnGetParentModificationUuid */
5580 iscsiGetParentModificationUuid,
5581 /* pfnSetParentModificationUuid */
5582 iscsiSetParentModificationUuid,
5583 /* pfnDump */
5584 iscsiDump,
5585 /* pfnGetTimeStamp */
5586 NULL,
5587 /* pfnGetParentTimeStamp */
5588 NULL,
5589 /* pfnSetParentTimeStamp */
5590 NULL,
5591 /* pfnGetParentFilename */
5592 NULL,
5593 /* pfnSetParentFilename */
5594 NULL,
5595 /* pfnIsAsyncIOSupported */
5596 iscsiIsAsyncIOSupported,
5597 /* pfnAsyncRead */
5598 iscsiAsyncRead,
5599 /* pfnAsyncWrite */
5600 iscsiAsyncWrite,
5601 /* pfnAsyncFlush */
5602 iscsiAsyncFlush,
5603 /* pfnComposeLocation */
5604 iscsiComposeLocation,
5605 /* pfnComposeName */
5606 iscsiComposeName,
5607 /* pfnCompact */
5608 NULL,
5609 /* pfnResize */
5610 NULL
5611};
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use