VirtualBox

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

Last change on this file since 41001 was 40229, checked in by vboxsync, 13 years ago

Storage/ISCSI: Don't validate the ExpStatSN field of a Login Respone with a non 0 status class because it is not valid. Fixes a crash when trying to login into a LIO target with wrong credentials.

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