VirtualBox

source: vbox/trunk/src/VBox/Storage/ISCSI.cpp@ 36230

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

iSCSI: Limit the number of certain error messages in the release log to prevent flooding it, reattach to the target if an unexpected error happens while sending a PDU

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

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