VirtualBox

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

最後變更 在這個檔案從32010是 31943,由 vboxsync 提交於 15 年 前

iSCSI: Clear pointer to next command to fix assertion

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette