VirtualBox

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

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

Storage: Implement offical support for other disk types like DVD and floppy images. DMG images can be used now without hacks

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

© 2023 Oracle
ContactPrivacy policyTerms of Use