VirtualBox

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

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

scm cleanup

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