VirtualBox

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

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

iSCSI: Build fix

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