VirtualBox

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

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

VD: Initial support to discard unused blocks in an image + support for VDI images

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