VirtualBox

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

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

iSCSI: Read timeout handling for poll to catch broken connections

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