VirtualBox

source: vbox/trunk/src/VBox/Storage/testcase/tstVDIo.cpp@ 36480

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

tstVDIo: New command for printing messages (no formatting) and a new I/O test script using data verification

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 82.5 KB
Line 
1/* $Id: tstVDIo.cpp 36135 2011-03-02 23:43:51Z vboxsync $ */
2/** @file
3 *
4 * VBox HDD container test utility - I/O replay.
5 */
6
7/*
8 * Copyright (C) 2011 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18#define LOGGROUP LOGGROUP_DEFAULT
19#include <VBox/vd.h>
20#include <VBox/err.h>
21#include <VBox/log.h>
22#include <iprt/asm.h>
23#include <iprt/string.h>
24#include <iprt/stream.h>
25#include <iprt/mem.h>
26#include <iprt/initterm.h>
27#include <iprt/getopt.h>
28#include <iprt/list.h>
29#include <iprt/ctype.h>
30#include <iprt/semaphore.h>
31#include <iprt/thread.h>
32#include <iprt/rand.h>
33#include <iprt/critsect.h>
34
35#include "VDMemDisk.h"
36#include "VDIoBackendMem.h"
37#include "VDIoRnd.h"
38
39/**
40 * A virtual file backed by memory.
41 */
42typedef struct VDFILE
43{
44 /** Pointer to the next file. */
45 RTLISTNODE Node;
46 /** Name of the file. */
47 char *pszName;
48 /** Memory file baking the file. */
49 PVDMEMDISK pMemDisk;
50 /** Flag whether the file is read locked. */
51 bool fReadLock;
52 /** Flag whether the file is write locked. */
53 bool fWriteLock;
54} VDFILE, *PVDFILE;
55
56/**
57 * VD storage object.
58 */
59typedef struct VDSTORAGE
60{
61 /** Pointer to the file. */
62 PVDFILE pFile;
63 /** Completion callback of the VD layer. */
64 PFNVDCOMPLETED pfnComplete;
65} VDSTORAGE, *PVDSTORAGE;
66
67/**
68 * A virtual disk.
69 */
70typedef struct VDDISK
71{
72 /** List node. */
73 RTLISTNODE ListNode;
74 /** Name of the disk handle for identification. */
75 char *pszName;
76 /** HDD handle to operate on. */
77 PVBOXHDD pVD;
78 /** Memory disk used for data verification. */
79 PVDMEMDISK pMemDiskVerify;
80 /** Critical section to serialize access to the memory disk. */
81 RTCRITSECT CritSectVerify;
82 /** Physical CHS Geometry. */
83 VDGEOMETRY PhysGeom;
84 /** Logical CHS geometry. */
85 VDGEOMETRY LogicalGeom;
86} VDDISK, *PVDDISK;
87
88/**
89 * Global VD test state.
90 */
91typedef struct VDTESTGLOB
92{
93 /** List of active virtual disks. */
94 RTLISTNODE ListDisks;
95 /** Head of the active file list. */
96 RTLISTNODE ListFiles;
97 /** Memory I/O backend. */
98 PVDIOBACKENDMEM pIoBackend;
99 /** Error interface. */
100 VDINTERFACE VDIError;
101 /** Error interface callbacks. */
102 VDINTERFACEERROR VDIErrorCallbacks;
103 /** Pointer to the per disk interface list. */
104 PVDINTERFACE pInterfacesDisk;
105 /** I/O interface. */
106 VDINTERFACE VDIIo;
107 /** I/O interface callbacks. */
108 VDINTERFACEIO VDIIoCallbacks;
109 /** Pointer to the per image interface list. */
110 PVDINTERFACE pInterfacesImages;
111 /** I/O RNG handle. */
112 PVDIORND pIoRnd;
113} VDTESTGLOB, *PVDTESTGLOB;
114
115/**
116 * Transfer direction.
117 */
118typedef enum VDIOREQTXDIR
119{
120 VDIOREQTXDIR_READ = 0,
121 VDIOREQTXDIR_WRITE,
122 VDIOREQTXDIR_FLUSH
123} VDIOREQTXDIR;
124
125/**
126 * I/O request.
127 */
128typedef struct VDIOREQ
129{
130 /** Transfer type. */
131 VDIOREQTXDIR enmTxDir;
132 /** slot index. */
133 unsigned idx;
134 /** Start offset. */
135 uint64_t off;
136 /** Size to transfer. */
137 size_t cbReq;
138 /** S/G Buffer */
139 RTSGBUF SgBuf;
140 /** Data segment */
141 RTSGSEG DataSeg;
142 /** Flag whether the request is outstanding or not. */
143 volatile bool fOutstanding;
144 /** Buffer to use for reads. */
145 void *pvBufRead;
146 /** Opaque user data. */
147 void *pvUser;
148} VDIOREQ, *PVDIOREQ;
149
150/**
151 * I/O test data.
152 */
153typedef struct VDIOTEST
154{
155 /** Start offset. */
156 uint64_t offStart;
157 /** End offset. */
158 uint64_t offEnd;
159 /** Flag whether random or sequential access is wanted */
160 bool fRandomAccess;
161 /** Block size. */
162 size_t cbBlkIo;
163 /** Number of bytes to transfer. */
164 uint64_t cbIo;
165 /** Chance in percent to get a write. */
166 unsigned uWriteChance;
167 /** Pointer to the I/O data generator. */
168 PVDIORND pIoRnd;
169 /** Data dependent on the I/O mode (sequential or random). */
170 union
171 {
172 /** Next offset for sequential access. */
173 uint64_t offNext;
174 /** Data for random acess. */
175 struct
176 {
177 /** Number of valid entries in the bitmap. */
178 uint32_t cBlocks;
179 /** Pointer to the bitmap marking accessed blocks. */
180 uint8_t *pbMapAccessed;
181 /** Number of unaccessed blocks. */
182 uint32_t cBlocksLeft;
183 } Rnd;
184 } u;
185} VDIOTEST, *PVDIOTEST;
186
187/**
188 * Argument types.
189 */
190typedef enum VDSCRIPTARGTYPE
191{
192 /** Argument is a string. */
193 VDSCRIPTARGTYPE_STRING = 0,
194 /** Argument is a 64bit unsigned number. */
195 VDSCRIPTARGTYPE_UNSIGNED_NUMBER,
196 /** Argument is a 64bit signed number. */
197 VDSCRIPTARGTYPE_SIGNED_NUMBER,
198 /** Arugment is a unsigned 64bit range */
199 VDSCRIPTARGTYPE_UNSIGNED_RANGE,
200 /** Arugment is a boolean. */
201 VDSCRIPTARGTYPE_BOOL
202} VDSCRIPTARGTYPE;
203
204/**
205 * Script argument.
206 */
207typedef struct VDSCRIPTARG
208{
209 /** Argument identifier. */
210 char chId;
211 /** Type of the argument. */
212 VDSCRIPTARGTYPE enmType;
213 /** Type depndent data. */
214 union
215 {
216 /** String. */
217 const char *pcszString;
218 /** Bool. */
219 bool fFlag;
220 /** unsigned number. */
221 uint64_t u64;
222 /** Signed number. */
223 int64_t i64;
224 /** Unsigned range. */
225 struct
226 {
227 uint64_t Start;
228 uint64_t End;
229 } Range;
230 } u;
231} VDSCRIPTARG, *PVDSCRIPTARG;
232
233/** Script action handler. */
234typedef DECLCALLBACK(int) FNVDSCRIPTACTION(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
235/** Pointer to a script action handler. */
236typedef FNVDSCRIPTACTION *PFNVDSCRIPTACTION;
237
238/**
239 * Script argument descriptor.
240 */
241typedef struct VDSCRIPTARGDESC
242{
243 /** Name of the arugment. */
244 const char *pcszName;
245 /** Identifier for the argument. */
246 char chId;
247 /** Type of the argument. */
248 VDSCRIPTARGTYPE enmType;
249 /** Flags */
250 uint32_t fFlags;
251} VDSCRIPTARGDESC, *PVDSCRIPTARGDESC;
252/** Pointer to a const script argument descriptor. */
253typedef const VDSCRIPTARGDESC *PCVDSCRIPTARGDESC;
254
255/** Flag whether the argument is mandatory. */
256#define VDSCRIPTARGDESC_FLAG_MANDATORY RT_BIT(0)
257/** Flag whether the number can have a size suffix (K|M|G) */
258#define VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX RT_BIT(1)
259
260/**
261 * Script action.
262 */
263typedef struct VDSCRIPTACTION
264{
265 /** Action name. */
266 const char *pcszAction;
267 /** Pointer to the arguments. */
268 const PCVDSCRIPTARGDESC paArgDesc;
269 /** Number of arugments in the array. */
270 unsigned cArgDescs;
271 /** Pointer to the action handler. */
272 PFNVDSCRIPTACTION pfnHandler;
273} VDSCRIPTACTION, *PVDSCRIPTACTION;
274
275typedef const VDSCRIPTACTION *PCVDSCRIPTACTION;
276
277static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
278static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
279static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
280static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
281static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
282static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
283static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
284static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
285static DECLCALLBACK(int) vdScriptHandlerSleep(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
286static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
287static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
288static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
289static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
290static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
291static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
292
293/* create action */
294const VDSCRIPTARGDESC g_aArgCreate[] =
295{
296 /* pcszName chId enmType fFlags */
297 {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
298 {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
299 {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
300 {"type", 't', VDSCRIPTARGTYPE_STRING, 0},
301 {"backend", 'b', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
302 {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX}
303};
304
305/* open action */
306const VDSCRIPTARGDESC g_aArgOpen[] =
307{
308 /* pcszName chId enmType fFlags */
309 {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
310 {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
311 {"backend", 'b', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
312 {"shareable", 's', VDSCRIPTARGTYPE_BOOL, 0},
313 {"readonly", 'r', VDSCRIPTARGTYPE_BOOL, 0}
314};
315
316/* I/O action */
317const VDSCRIPTARGDESC g_aArgIo[] =
318{
319 /* pcszName chId enmType fFlags */
320 {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
321 {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0},
322 {"max-reqs", 'l', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0},
323 {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
324 {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
325 {"blocksize", 'b', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
326 {"off", 'o', VDSCRIPTARGTYPE_UNSIGNED_RANGE, VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
327 {"writes", 'w', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}
328};
329
330/* flush action */
331const VDSCRIPTARGDESC g_aArgFlush[] =
332{
333 /* pcszName chId enmType fFlags */
334 {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
335 {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0}
336};
337
338/* merge action */
339const VDSCRIPTARGDESC g_aArgMerge[] =
340{
341 /* pcszName chId enmType fFlags */
342 {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
343 {"forward", 'f', VDSCRIPTARGTYPE_BOOL, VDSCRIPTARGDESC_FLAG_MANDATORY}
344};
345
346/* close action */
347const VDSCRIPTARGDESC g_aArgClose[] =
348{
349 /* pcszName chId enmType fFlags */
350 {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
351 {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
352 {"delete", 'r', VDSCRIPTARGTYPE_BOOL, VDSCRIPTARGDESC_FLAG_MANDATORY}
353};
354
355/* I/O RNG create action */
356const VDSCRIPTARGDESC g_aArgIoRngCreate[] =
357{
358 /* pcszName chId enmType fFlags */
359 {"size", 'd', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
360 {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
361 {"seed", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0}
362};
363
364/* Sleep */
365const VDSCRIPTARGDESC g_aArgSleep[] =
366{
367 /* pcszName chId enmType fFlags */
368 {"time", 't', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}
369};
370
371/* Dump memory file */
372const VDSCRIPTARGDESC g_aArgDumpFile[] =
373{
374 /* pcszName chId enmType fFlags */
375 {"file", 'f', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
376 {"path", 'p', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
377};
378
379/* Create virtual disk handle */
380const VDSCRIPTARGDESC g_aArgCreateDisk[] =
381{
382 /* pcszName chId enmType fFlags */
383 {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
384 {"verify", 'v', VDSCRIPTARGTYPE_BOOL, 0}
385};
386
387/* Create virtual disk handle */
388const VDSCRIPTARGDESC g_aArgDestroyDisk[] =
389{
390 /* pcszName chId enmType fFlags */
391 {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
392};
393
394/* Compare virtual disks */
395const VDSCRIPTARGDESC g_aArgCompareDisks[] =
396{
397 /* pcszName chId enmType fFlags */
398 {"disk1", '1', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
399 {"disk2", '2', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
400};
401
402/* Dump disk info */
403const VDSCRIPTARGDESC g_aArgDumpDiskInfo[] =
404{
405 /* pcszName chId enmType fFlags */
406 {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
407};
408
409/* Print message */
410const VDSCRIPTARGDESC g_aArgPrintMsg[] =
411{
412 /* pcszName chId enmType fFlags */
413 {"msg", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
414};
415
416const VDSCRIPTACTION g_aScriptActions[] =
417{
418 /* pcszAction paArgDesc cArgDescs pfnHandler */
419 {"create", g_aArgCreate, RT_ELEMENTS(g_aArgCreate), vdScriptHandlerCreate},
420 {"open", g_aArgOpen, RT_ELEMENTS(g_aArgOpen), vdScriptHandlerOpen},
421 {"io", g_aArgIo, RT_ELEMENTS(g_aArgIo), vdScriptHandlerIo},
422 {"flush", g_aArgFlush, RT_ELEMENTS(g_aArgFlush), vdScriptHandlerFlush},
423 {"close", g_aArgClose, RT_ELEMENTS(g_aArgClose), vdScriptHandlerClose},
424 {"merge", g_aArgMerge, RT_ELEMENTS(g_aArgMerge), vdScriptHandlerMerge},
425 {"iorngcreate", g_aArgIoRngCreate, RT_ELEMENTS(g_aArgIoRngCreate), vdScriptHandlerIoRngCreate},
426 {"iorngdestroy", NULL, 0, vdScriptHandlerIoRngDestroy},
427 {"sleep", g_aArgSleep, RT_ELEMENTS(g_aArgSleep), vdScriptHandlerSleep},
428 {"dumpfile", g_aArgDumpFile, RT_ELEMENTS(g_aArgDumpFile), vdScriptHandlerDumpFile},
429 {"createdisk", g_aArgCreateDisk, RT_ELEMENTS(g_aArgCreateDisk), vdScriptHandlerCreateDisk},
430 {"destroydisk", g_aArgDestroyDisk, RT_ELEMENTS(g_aArgDestroyDisk), vdScriptHandlerDestroyDisk},
431 {"comparedisks", g_aArgCompareDisks, RT_ELEMENTS(g_aArgCompareDisks), vdScriptHandlerCompareDisks},
432 {"dumpdiskinfo", g_aArgDumpDiskInfo, RT_ELEMENTS(g_aArgDumpDiskInfo), vdScriptHandlerDumpDiskInfo},
433 {"print", g_aArgPrintMsg, RT_ELEMENTS(g_aArgPrintMsg), vdScriptHandlerPrintMsg}
434};
435
436const unsigned g_cScriptActions = RT_ELEMENTS(g_aScriptActions);
437
438static void tstVDError(void *pvUser, int rc, RT_SRC_POS_DECL,
439 const char *pszFormat, va_list va)
440{
441 RTPrintf("tstVD: Error %Rrc at %s:%u (%s): ", rc, RT_SRC_POS_ARGS);
442 RTPrintfV(pszFormat, va);
443 RTPrintf("\n");
444}
445
446static int tstVDMessage(void *pvUser, const char *pszFormat, va_list va)
447{
448 RTPrintf("tstVD: ");
449 RTPrintfV(pszFormat, va);
450 return VINF_SUCCESS;
451}
452
453static int tstVDIoTestInit(PVDIOTEST pIoTest, PVDTESTGLOB pGlob, bool fRandomAcc, uint64_t cbIo,
454 size_t cbBlkSize, uint64_t offStart, uint64_t offEnd,
455 unsigned uWriteChance);
456static bool tstVDIoTestRunning(PVDIOTEST pIoTest);
457static void tstVDIoTestDestroy(PVDIOTEST pIoTest);
458static bool tstVDIoTestReqOutstanding(PVDIOREQ pIoReq);
459static int tstVDIoTestReqInit(PVDIOTEST pIoTest, PVDIOREQ pIoReq, void *pvUser);
460static void tstVDIoTestReqComplete(void *pvUser1, void *pvUser2, int rcReq);
461
462static PVDDISK tstVDIoGetDiskByName(PVDTESTGLOB pGlob, const char *pcszDisk);
463
464static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
465{
466 int rc = VINF_SUCCESS;
467 uint64_t cbSize = 0;
468 const char *pcszBackend = NULL;
469 const char *pcszImage = NULL;
470 const char *pcszDisk = NULL;
471 PVDDISK pDisk = NULL;
472 bool fBase = false;
473 bool fDynamic = true;
474
475 for (unsigned i = 0; i < cScriptArgs; i++)
476 {
477 switch (paScriptArgs[i].chId)
478 {
479 case 'd':
480 {
481 pcszDisk = paScriptArgs[i].u.pcszString;
482 break;
483 }
484 case 'm':
485 {
486 if (!RTStrICmp(paScriptArgs[i].u.pcszString, "base"))
487 fBase = true;
488 else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "diff"))
489 fBase = false;
490 else
491 {
492 RTPrintf("Invalid image mode '%s' given\n", paScriptArgs[i].u.pcszString);
493 rc = VERR_INVALID_PARAMETER;
494 }
495 break;
496 }
497 case 'n':
498 {
499 pcszImage = paScriptArgs[i].u.pcszString;
500 break;
501 }
502 case 'b':
503 {
504 pcszBackend = paScriptArgs[i].u.pcszString;
505 break;
506 }
507 case 's':
508 {
509 cbSize = paScriptArgs[i].u.u64;
510 break;
511 }
512 case 't':
513 {
514 if (!RTStrICmp(paScriptArgs[i].u.pcszString, "fixed"))
515 fDynamic = false;
516 else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "dynamic"))
517 fDynamic = true;
518 else
519 {
520 RTPrintf("Invalid image type '%s' given\n", paScriptArgs[i].u.pcszString);
521 rc = VERR_INVALID_PARAMETER;
522 }
523 break;
524 }
525 default:
526 AssertMsgFailed(("Invalid argument given!\n"));
527 }
528
529 if (RT_FAILURE(rc))
530 break;
531 }
532
533 if (RT_SUCCESS(rc))
534 {
535 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
536 if (pDisk)
537 {
538 unsigned fImageFlags = VD_IMAGE_FLAGS_NONE;
539
540 if (!fDynamic)
541 fImageFlags |= VD_IMAGE_FLAGS_FIXED;
542
543 if (fBase)
544 rc = VDCreateBase(pDisk->pVD, pcszBackend, pcszImage, cbSize, fImageFlags, NULL,
545 &pDisk->PhysGeom, &pDisk->LogicalGeom,
546 NULL, VD_OPEN_FLAGS_ASYNC_IO, pGlob->pInterfacesImages, NULL);
547 else
548 rc = VDCreateDiff(pDisk->pVD, pcszBackend, pcszImage, fImageFlags, NULL, NULL, NULL, VD_OPEN_FLAGS_ASYNC_IO,
549 pGlob->pInterfacesImages, NULL);
550 }
551 else
552 rc = VERR_NOT_FOUND;
553 }
554
555 return rc;
556}
557
558static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
559{
560 int rc = VINF_SUCCESS;
561 const char *pcszBackend = NULL;
562 const char *pcszImage = NULL;
563 const char *pcszDisk = NULL;
564 PVDDISK pDisk = NULL;
565 bool fShareable = false;
566 bool fReadonly = false;
567
568 for (unsigned i = 0; i < cScriptArgs; i++)
569 {
570 switch (paScriptArgs[i].chId)
571 {
572 case 'd':
573 {
574 pcszDisk = paScriptArgs[i].u.pcszString;
575 break;
576 }
577 case 'n':
578 {
579 pcszImage = paScriptArgs[i].u.pcszString;
580 break;
581 }
582 case 'b':
583 {
584 pcszBackend = paScriptArgs[i].u.pcszString;
585 break;
586 }
587 case 's':
588 {
589 fShareable = paScriptArgs[i].u.fFlag;
590 break;
591 }
592 case 'r':
593 {
594 fReadonly = paScriptArgs[i].u.fFlag;
595 break;
596 }
597 default:
598 AssertMsgFailed(("Invalid argument given!\n"));
599 }
600
601 if (RT_FAILURE(rc))
602 break;
603 }
604
605 if (RT_SUCCESS(rc))
606 {
607 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
608 if (pDisk)
609 {
610 unsigned fOpenFlags = VD_OPEN_FLAGS_ASYNC_IO;
611
612 if (fShareable)
613 fOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
614 if (fReadonly)
615 fOpenFlags |= VD_OPEN_FLAGS_READONLY;
616
617 rc = VDOpen(pDisk->pVD, pcszBackend, pcszImage, fOpenFlags, pGlob->pInterfacesImages);
618 }
619 else
620 rc = VERR_NOT_FOUND;
621 }
622
623 return rc;
624}
625
626static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
627{
628 int rc = VINF_SUCCESS;
629 bool fAsync = false;
630 bool fRandomAcc = false;
631 uint64_t cbIo = 0;
632 uint64_t cbBlkSize = 0;
633 bool fDataProviderRnd = false;
634 bool fPrintStats = false;
635 uint64_t offStart = 0;
636 uint64_t offEnd = 0;
637 unsigned cMaxReqs = 0;
638 uint8_t uWriteChance = 0;
639 const char *pcszDisk = NULL;
640 PVDDISK pDisk = NULL;
641
642 for (unsigned i = 0; i < cScriptArgs; i++)
643 {
644 switch (paScriptArgs[i].chId)
645 {
646 case 'd':
647 {
648 pcszDisk = paScriptArgs[i].u.pcszString;
649 break;
650 }
651 case 'a':
652 {
653 fAsync = paScriptArgs[i].u.fFlag;
654 break;
655 }
656 case 'l':
657 {
658 cMaxReqs = paScriptArgs[i].u.u64;
659 break;
660 }
661 case 'm':
662 {
663 if (!RTStrICmp(paScriptArgs[i].u.pcszString, "seq"))
664 fRandomAcc = false;
665 else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "rnd"))
666 fRandomAcc = true;
667 else
668 {
669 RTPrintf("Invalid access mode '%s'\n", paScriptArgs[i].u.pcszString);
670 rc = VERR_INVALID_PARAMETER;
671 }
672 break;
673 }
674 case 's':
675 {
676 cbIo = paScriptArgs[i].u.u64;
677 break;
678 }
679 case 'b':
680 {
681 cbBlkSize = paScriptArgs[i].u.u64;
682 break;
683 }
684 case 'o':
685 {
686 offStart = paScriptArgs[i].u.Range.Start;
687 offEnd = paScriptArgs[i].u.Range.End;
688 break;
689 }
690 case 'w':
691 {
692 uWriteChance = (uint8_t)paScriptArgs[i].u.u64;
693 break;
694 }
695 default:
696 AssertMsgFailed(("Invalid argument given!\n"));
697 }
698
699 if (RT_FAILURE(rc))
700 break;
701 }
702
703 if (RT_SUCCESS(rc))
704 {
705 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
706 if (!pDisk)
707 rc = VERR_NOT_FOUND;
708 }
709
710 if (RT_SUCCESS(rc))
711 {
712 /* Set defaults if not set by the user. */
713 if (offStart == 0 && offEnd == 0)
714 {
715 offEnd = VDGetSize(pDisk->pVD, VD_LAST_IMAGE);
716 if (offEnd == 0)
717 return VERR_INVALID_STATE;
718 }
719
720 if (!cbIo)
721 cbIo = offEnd;
722 }
723
724 if (RT_SUCCESS(rc))
725 {
726 VDIOTEST IoTest;
727
728 rc = tstVDIoTestInit(&IoTest, pGlob, fRandomAcc, cbIo, cbBlkSize, offStart, offEnd, uWriteChance);
729 if (RT_SUCCESS(rc))
730 {
731 PVDIOREQ paIoReq = NULL;
732 unsigned cMaxTasksOutstanding = fAsync ? cMaxReqs : 1;
733 RTSEMEVENT EventSem;
734
735 rc = RTSemEventCreate(&EventSem);
736 paIoReq = (PVDIOREQ)RTMemAllocZ(cMaxTasksOutstanding * sizeof(VDIOREQ));
737 if (paIoReq && RT_SUCCESS(rc))
738 {
739 uint64_t NanoTS = RTTimeNanoTS();
740
741 /* Init requests. */
742 for (unsigned i = 0; i < cMaxTasksOutstanding; i++)
743 {
744 paIoReq[i].idx = i;
745 paIoReq[i].pvBufRead = RTMemAlloc(cbBlkSize);
746 if (!paIoReq[i].pvBufRead)
747 {
748 rc = VERR_NO_MEMORY;
749 break;
750 }
751 }
752
753 while ( tstVDIoTestRunning(&IoTest)
754 && RT_SUCCESS(rc))
755 {
756 bool fTasksOutstanding = false;
757 unsigned idx = 0;
758
759 /* Submit all idling requests. */
760 while ( idx < cMaxTasksOutstanding
761 && tstVDIoTestRunning(&IoTest))
762 {
763 if (!tstVDIoTestReqOutstanding(&paIoReq[idx]))
764 {
765 rc = tstVDIoTestReqInit(&IoTest, &paIoReq[idx], pDisk);
766 AssertRC(rc);
767
768 if (RT_SUCCESS(rc))
769 {
770 if (!fAsync)
771 {
772 switch (paIoReq[idx].enmTxDir)
773 {
774 case VDIOREQTXDIR_READ:
775 {
776 rc = VDRead(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].DataSeg.pvSeg, paIoReq[idx].cbReq);
777
778 if (RT_SUCCESS(rc)
779 && pDisk->pMemDiskVerify)
780 {
781 RTSGBUF SgBuf;
782 RTSgBufInit(&SgBuf, &paIoReq[idx].DataSeg, 1);
783
784 if (VDMemDiskCmp(pDisk->pMemDiskVerify, paIoReq[idx].off, paIoReq[idx].cbReq, &SgBuf))
785 {
786 RTPrintf("Corrupted disk at offset %llu!\n", paIoReq[idx].off);
787 rc = VERR_INVALID_STATE;
788 }
789 }
790 break;
791 }
792 case VDIOREQTXDIR_WRITE:
793 {
794 rc = VDWrite(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].DataSeg.pvSeg, paIoReq[idx].cbReq);
795
796 if (RT_SUCCESS(rc)
797 && pDisk->pMemDiskVerify)
798 {
799 RTSGBUF SgBuf;
800 RTSgBufInit(&SgBuf, &paIoReq[idx].DataSeg, 1);
801 rc = VDMemDiskWrite(pDisk->pMemDiskVerify, paIoReq[idx].off, paIoReq[idx].cbReq, &SgBuf);
802 }
803 break;
804 }
805 case VDIOREQTXDIR_FLUSH:
806 {
807 rc = VDFlush(pDisk->pVD);
808 break;
809 }
810 }
811 if (RT_SUCCESS(rc))
812 idx++;
813 }
814 else
815 {
816 switch (paIoReq[idx].enmTxDir)
817 {
818 case VDIOREQTXDIR_READ:
819 {
820 rc = VDAsyncRead(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].cbReq, &paIoReq[idx].SgBuf,
821 tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
822 break;
823 }
824 case VDIOREQTXDIR_WRITE:
825 {
826 rc = VDAsyncWrite(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].cbReq, &paIoReq[idx].SgBuf,
827 tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
828 break;
829 }
830 case VDIOREQTXDIR_FLUSH:
831 {
832 rc = VDAsyncFlush(pDisk->pVD, tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
833 break;
834 }
835 }
836
837 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
838 {
839 idx++;
840 fTasksOutstanding = true;
841 rc = VINF_SUCCESS;
842 }
843 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
844 {
845 switch (paIoReq[idx].enmTxDir)
846 {
847 case VDIOREQTXDIR_READ:
848 {
849 rc = VDAsyncRead(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].cbReq, &paIoReq[idx].SgBuf,
850 tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
851 if (pDisk->pMemDiskVerify)
852 {
853 RTCritSectEnter(&pDisk->CritSectVerify);
854 RTSgBufReset(&paIoReq[idx].SgBuf);
855
856 if (VDMemDiskCmp(pDisk->pMemDiskVerify, paIoReq[idx].off, paIoReq[idx].cbReq,
857 &paIoReq[idx].SgBuf))
858 {
859 RTPrintf("Corrupted disk at offset %llu!\n", paIoReq[idx].off);
860 rc = VERR_INVALID_STATE;
861 }
862 RTCritSectLeave(&pDisk->CritSectVerify);
863 }
864 }
865 case VDIOREQTXDIR_WRITE:
866 {
867 rc = VDAsyncWrite(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].cbReq, &paIoReq[idx].SgBuf,
868 tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
869
870 if (pDisk->pMemDiskVerify)
871 {
872 RTCritSectEnter(&pDisk->CritSectVerify);
873 RTSgBufReset(&paIoReq[idx].SgBuf);
874
875 rc = VDMemDiskWrite(pDisk->pMemDiskVerify, paIoReq[idx].off, paIoReq[idx].cbReq,
876 &paIoReq[idx].SgBuf);
877 RTCritSectLeave(&pDisk->CritSectVerify);
878 }
879 break;
880 }
881 case VDIOREQTXDIR_FLUSH:
882 break;
883 }
884
885 ASMAtomicXchgBool(&paIoReq[idx].fOutstanding, false);
886 if (rc != VERR_INVALID_STATE)
887 rc = VINF_SUCCESS;
888 }
889 }
890
891 if (RT_FAILURE(rc))
892 RTPrintf("Error submitting task %u rc=%Rrc\n", paIoReq[idx].idx, rc);
893 }
894 }
895 }
896
897 /* Wait for a request to complete. */
898 if ( fAsync
899 && fTasksOutstanding)
900 {
901 rc = RTSemEventWait(EventSem, RT_INDEFINITE_WAIT);
902 AssertRC(rc);
903 }
904 }
905
906 /* Cleanup, wait for all tasks to complete. */
907 while (fAsync)
908 {
909 unsigned idx = 0;
910 bool fAllIdle = true;
911
912 while (idx < cMaxTasksOutstanding)
913 {
914 if (tstVDIoTestReqOutstanding(&paIoReq[idx]))
915 {
916 fAllIdle = false;
917 break;
918 }
919 idx++;
920 }
921
922 if (!fAllIdle)
923 {
924 rc = RTSemEventWait(EventSem, 100);
925 Assert(RT_SUCCESS(rc) || rc == VERR_TIMEOUT);
926 }
927 else
928 break;
929 }
930
931 NanoTS = RTTimeNanoTS() - NanoTS;
932 uint64_t SpeedKBs = (uint64_t)(cbIo / (NanoTS / 1000000000.0) / 1024);
933 RTPrintf("I/O Test: Throughput %lld kb/s\n", SpeedKBs);
934
935 RTSemEventDestroy(EventSem);
936 RTMemFree(paIoReq);
937 }
938 else
939 rc = VERR_NO_MEMORY;
940
941 tstVDIoTestDestroy(&IoTest);
942 }
943 }
944
945 return rc;
946}
947
948static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
949{
950 int rc = VINF_SUCCESS;
951 bool fAsync = false;
952 const char *pcszDisk = NULL;
953 PVDDISK pDisk = NULL;
954
955 for (unsigned i = 0; i < cScriptArgs; i++)
956 {
957 switch (paScriptArgs[i].chId)
958 {
959 case 'd':
960 {
961 pcszDisk = paScriptArgs[i].u.pcszString;
962 break;
963 }
964 case 'a':
965 {
966 fAsync = paScriptArgs[i].u.fFlag;
967 break;
968 }
969
970 default:
971 AssertMsgFailed(("Invalid argument given!\n"));
972 }
973
974 if (RT_FAILURE(rc))
975 break;
976 }
977
978 if (RT_SUCCESS(rc))
979 {
980 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
981 if (!pDisk)
982 rc = VERR_NOT_FOUND;
983 else if (fAsync)
984 {
985 /** @todo */
986 rc = VERR_NOT_IMPLEMENTED;
987 }
988 else
989 rc = VDFlush(pDisk->pVD);
990 }
991
992 return rc;
993}
994
995static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
996{
997 return VERR_NOT_IMPLEMENTED;
998}
999
1000static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1001{
1002 int rc = VINF_SUCCESS;
1003 bool fAll = false;
1004 bool fDelete = false;
1005 const char *pcszDisk = NULL;
1006 PVDDISK pDisk = NULL;
1007
1008 for (unsigned i = 0; i < cScriptArgs; i++)
1009 {
1010 switch (paScriptArgs[i].chId)
1011 {
1012 case 'd':
1013 {
1014 pcszDisk = paScriptArgs[i].u.pcszString;
1015 break;
1016 }
1017 case 'm':
1018 {
1019 if (!RTStrICmp(paScriptArgs[i].u.pcszString, "all"))
1020 fAll = true;
1021 else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "single"))
1022 fAll = false;
1023 else
1024 {
1025 RTPrintf("Invalid mode '%s' given\n", paScriptArgs[i].u.pcszString);
1026 rc = VERR_INVALID_PARAMETER;
1027 }
1028 break;
1029 }
1030 case 'r':
1031 {
1032 fDelete = paScriptArgs[i].u.fFlag;
1033 break;
1034 }
1035 default:
1036 AssertMsgFailed(("Invalid argument given!\n"));
1037 }
1038
1039 if (RT_FAILURE(rc))
1040 break;
1041 }
1042
1043 if ( RT_SUCCESS(rc)
1044 && fAll
1045 && fDelete)
1046 {
1047 RTPrintf("mode=all doesn't work with delete=yes\n");
1048 rc = VERR_INVALID_PARAMETER;
1049 }
1050
1051 if (RT_SUCCESS(rc))
1052 {
1053 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1054 if (pDisk)
1055 {
1056 if (fAll)
1057 rc = VDCloseAll(pDisk->pVD);
1058 else
1059 rc = VDClose(pDisk->pVD, fDelete);
1060 }
1061 else
1062 rc = VERR_NOT_FOUND;
1063 }
1064 return rc;
1065}
1066
1067
1068static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1069{
1070 int rc = VINF_SUCCESS;
1071 size_t cbPattern = 0;
1072 uint64_t uSeed = 0;
1073 const char *pcszSeeder = NULL;
1074
1075 for (unsigned i = 0; i < cScriptArgs; i++)
1076 {
1077 switch (paScriptArgs[i].chId)
1078 {
1079 case 'd':
1080 {
1081 cbPattern = paScriptArgs[i].u.u64;
1082 break;
1083 }
1084 case 's':
1085 {
1086 uSeed = paScriptArgs[i].u.u64;
1087 break;
1088 }
1089 case 'm':
1090 {
1091 pcszSeeder = paScriptArgs[i].u.pcszString;
1092 break;
1093 }
1094 default:
1095 AssertMsgFailed(("Invalid argument given!\n"));
1096 }
1097 }
1098
1099 if (pGlob->pIoRnd)
1100 {
1101 RTPrintf("I/O RNG already exists\n");
1102 rc = VERR_INVALID_STATE;
1103 }
1104 else
1105 {
1106 uint64_t uSeedToUse = 0;
1107
1108 if (!RTStrICmp(pcszSeeder, "manual"))
1109 uSeedToUse = uSeed;
1110 else if (!RTStrICmp(pcszSeeder, "time"))
1111 uSeedToUse = RTTimeSystemMilliTS();
1112 else if (!RTStrICmp(pcszSeeder, "system"))
1113 {
1114 RTRAND hRand;
1115 rc = RTRandAdvCreateSystemTruer(&hRand);
1116 if (RT_SUCCESS(rc))
1117 {
1118 RTRandAdvBytes(hRand, &uSeedToUse, sizeof(uSeedToUse));
1119 RTRandAdvDestroy(hRand);
1120 }
1121 }
1122
1123 if (RT_SUCCESS(rc))
1124 rc = VDIoRndCreate(&pGlob->pIoRnd, cbPattern, uSeed);
1125 }
1126
1127 return rc;
1128}
1129
1130static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1131{
1132 if (pGlob->pIoRnd)
1133 {
1134 VDIoRndDestroy(pGlob->pIoRnd);
1135 pGlob->pIoRnd = NULL;
1136 }
1137 else
1138 RTPrintf("WARNING: No I/O RNG active, faulty script. Continuing\n");
1139
1140 return VINF_SUCCESS;
1141}
1142
1143static DECLCALLBACK(int) vdScriptHandlerSleep(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1144{
1145 int rc = VINF_SUCCESS;
1146 uint64_t cMillies = 0;
1147
1148 for (unsigned i = 0; i < cScriptArgs; i++)
1149 {
1150 switch (paScriptArgs[i].chId)
1151 {
1152 case 't':
1153 {
1154 cMillies = paScriptArgs[i].u.u64;
1155 break;
1156 }
1157 default:
1158 AssertMsgFailed(("Invalid argument given!\n"));
1159 }
1160 }
1161
1162 rc = RTThreadSleep(cMillies);
1163 return rc;
1164}
1165
1166static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1167{
1168 int rc = VINF_SUCCESS;
1169 const char *pcszFile = NULL;
1170 const char *pcszPathToDump = NULL;
1171
1172 for (unsigned i = 0; i < cScriptArgs; i++)
1173 {
1174 switch (paScriptArgs[i].chId)
1175 {
1176 case 'f':
1177 {
1178 pcszFile = paScriptArgs[i].u.pcszString;
1179 break;
1180 }
1181 case 'p':
1182 {
1183 pcszPathToDump = paScriptArgs[i].u.pcszString;
1184 break;
1185 }
1186 default:
1187 AssertMsgFailed(("Invalid argument given!\n"));
1188 }
1189 }
1190
1191 /* Check for the file. */
1192 PVDFILE pIt = NULL;
1193 bool fFound = false;
1194 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1195 {
1196 if (!RTStrCmp(pIt->pszName, pcszFile))
1197 {
1198 fFound = true;
1199 break;
1200 }
1201 }
1202
1203 if (fFound)
1204 {
1205 RTPrintf("Dumping memory file %s to %s, this might take some time\n", pcszFile, pcszPathToDump);
1206 rc = VDMemDiskWriteToFile(pIt->pMemDisk, pcszPathToDump);
1207 }
1208 else
1209 rc = VERR_FILE_NOT_FOUND;
1210
1211 return rc;
1212}
1213
1214static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1215{
1216 int rc = VINF_SUCCESS;
1217 const char *pcszDisk = NULL;
1218 PVDDISK pDisk = NULL;
1219 bool fVerify = false;
1220
1221 for (unsigned i = 0; i < cScriptArgs; i++)
1222 {
1223 switch (paScriptArgs[i].chId)
1224 {
1225 case 'n':
1226 {
1227 pcszDisk = paScriptArgs[i].u.pcszString;
1228 break;
1229 }
1230 case 'v':
1231 {
1232 fVerify = paScriptArgs[i].u.fFlag;
1233 break;
1234 }
1235 default:
1236 AssertMsgFailed(("Invalid argument given!\n"));
1237 }
1238 }
1239
1240 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1241 if (pDisk)
1242 rc = VERR_ALREADY_EXISTS;
1243 else
1244 {
1245 pDisk = (PVDDISK)RTMemAllocZ(sizeof(VDDISK));
1246 if (pDisk)
1247 {
1248 pDisk->pszName = RTStrDup(pcszDisk);
1249 if (pDisk->pszName)
1250 {
1251 rc = VINF_SUCCESS;
1252
1253 if (fVerify)
1254 {
1255 rc = VDMemDiskCreate(&pDisk->pMemDiskVerify, 0 /* Growing */);
1256 if (RT_SUCCESS(rc))
1257 {
1258 rc = RTCritSectInit(&pDisk->CritSectVerify);
1259 if (RT_FAILURE(rc))
1260 VDMemDiskDestroy(pDisk->pMemDiskVerify);
1261 }
1262 }
1263
1264 if (RT_SUCCESS(rc))
1265 {
1266 rc = VDCreate(pGlob->pInterfacesDisk, VDTYPE_HDD, &pDisk->pVD);
1267
1268 if (RT_SUCCESS(rc))
1269 RTListAppend(&pGlob->ListDisks, &pDisk->ListNode);
1270 else
1271 {
1272 if (fVerify)
1273 {
1274 RTCritSectDelete(&pDisk->CritSectVerify);
1275 VDMemDiskDestroy(pDisk->pMemDiskVerify);
1276 }
1277 RTStrFree(pDisk->pszName);
1278 }
1279 }
1280 }
1281 else
1282 rc = VERR_NO_MEMORY;
1283
1284 if (RT_FAILURE(rc))
1285 RTMemFree(pDisk);
1286 }
1287 else
1288 rc = VERR_NO_MEMORY;
1289 }
1290 return rc;
1291}
1292
1293static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1294{
1295 int rc = VINF_SUCCESS;
1296 const char *pcszDisk = NULL;
1297 PVDDISK pDisk = NULL;
1298
1299 for (unsigned i = 0; i < cScriptArgs; i++)
1300 {
1301 switch (paScriptArgs[i].chId)
1302 {
1303 case 'n':
1304 {
1305 pcszDisk = paScriptArgs[i].u.pcszString;
1306 break;
1307 }
1308 default:
1309 AssertMsgFailed(("Invalid argument given!\n"));
1310 }
1311 }
1312
1313 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1314 if (pDisk)
1315 {
1316 RTListNodeRemove(&pDisk->ListNode);
1317 VDDestroy(pDisk->pVD);
1318 if (pDisk->pMemDiskVerify)
1319 {
1320 VDMemDiskDestroy(pDisk->pMemDiskVerify);
1321 RTCritSectDelete(&pDisk->CritSectVerify);
1322 }
1323 RTStrFree(pDisk->pszName);
1324 RTMemFree(pDisk);
1325 }
1326 else
1327 rc = VERR_NOT_FOUND;
1328
1329 return rc;
1330}
1331
1332static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1333{
1334 int rc = VINF_SUCCESS;
1335 const char *pcszDisk1 = NULL;
1336 PVDDISK pDisk1 = NULL;
1337 const char *pcszDisk2 = NULL;
1338 PVDDISK pDisk2 = NULL;
1339
1340 for (unsigned i = 0; i < cScriptArgs; i++)
1341 {
1342 switch (paScriptArgs[i].chId)
1343 {
1344 case '1':
1345 {
1346 pcszDisk1 = paScriptArgs[i].u.pcszString;
1347 break;
1348 }
1349 case '2':
1350 {
1351 pcszDisk2 = paScriptArgs[i].u.pcszString;
1352 break;
1353 }
1354 default:
1355 AssertMsgFailed(("Invalid argument given!\n"));
1356 }
1357 }
1358
1359 pDisk1 = tstVDIoGetDiskByName(pGlob, pcszDisk1);
1360 pDisk2 = tstVDIoGetDiskByName(pGlob, pcszDisk2);
1361
1362 if (pDisk1 && pDisk2)
1363 {
1364 uint8_t *pbBuf1 = (uint8_t *)RTMemAllocZ(16 * _1M);
1365 uint8_t *pbBuf2 = (uint8_t *)RTMemAllocZ(16 * _1M);
1366 if (pbBuf1 && pbBuf2)
1367 {
1368 uint64_t cbDisk1, cbDisk2;
1369 uint64_t uOffCur = 0;
1370
1371 cbDisk1 = VDGetSize(pDisk1->pVD, VD_LAST_IMAGE);
1372 cbDisk2 = VDGetSize(pDisk2->pVD, VD_LAST_IMAGE);
1373
1374 if (cbDisk1 != cbDisk2)
1375 RTPrintf("Disks differ in size %llu vs %llu\n", cbDisk1, cbDisk2);
1376 else
1377 {
1378 while (uOffCur < cbDisk1)
1379 {
1380 size_t cbRead = RT_MIN(cbDisk1, 16 * _1M);
1381
1382 rc = VDRead(pDisk1->pVD, uOffCur, pbBuf1, cbRead);
1383 if (RT_SUCCESS(rc))
1384 rc = VDRead(pDisk2->pVD, uOffCur, pbBuf2, cbRead);
1385
1386 if (RT_SUCCESS(rc))
1387 {
1388 if (memcmp(pbBuf1, pbBuf2, cbRead))
1389 {
1390 RTPrintf("Disks differ at offset %llu\n", uOffCur);
1391 rc = VERR_DEV_IO_ERROR;
1392 break;
1393 }
1394 }
1395 else
1396 {
1397 RTPrintf("Reading one disk at offset %llu failed\n", uOffCur);
1398 break;
1399 }
1400
1401 uOffCur += cbRead;
1402 cbDisk1 -= cbRead;
1403 }
1404 }
1405 RTMemFree(pbBuf1);
1406 RTMemFree(pbBuf2);
1407 }
1408 else
1409 {
1410 if (pbBuf1)
1411 RTMemFree(pbBuf1);
1412 if (pbBuf2)
1413 RTMemFree(pbBuf2);
1414 rc = VERR_NO_MEMORY;
1415 }
1416 }
1417 else
1418 rc = VERR_NOT_FOUND;
1419
1420 return rc;
1421}
1422
1423static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1424{
1425 int rc = VINF_SUCCESS;
1426 const char *pcszDisk = NULL;
1427 PVDDISK pDisk = NULL;
1428
1429 for (unsigned i = 0; i < cScriptArgs; i++)
1430 {
1431 switch (paScriptArgs[i].chId)
1432 {
1433 case 'd':
1434 {
1435 pcszDisk = paScriptArgs[i].u.pcszString;
1436 break;
1437 }
1438 default:
1439 AssertMsgFailed(("Invalid argument given!\n"));
1440 }
1441 }
1442
1443 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1444
1445 if (pDisk)
1446 VDDumpImages(pDisk->pVD);
1447 else
1448 rc = VERR_NOT_FOUND;
1449
1450 return rc;
1451}
1452
1453static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1454{
1455 RTPrintf("%s\n", paScriptArgs[0].u.pcszString);
1456 return VINF_SUCCESS;
1457}
1458
1459static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation,
1460 uint32_t fOpen,
1461 PFNVDCOMPLETED pfnCompleted,
1462 void **ppStorage)
1463{
1464 int rc = VINF_SUCCESS;
1465 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1466 bool fFound = false;
1467
1468 /*
1469 * Some backends use ./ for paths, strip it.
1470 * @todo: Implement proper directory support for the
1471 * memory filesystem.
1472 */
1473 if ( strlen(pszLocation) >= 2
1474 && *pszLocation == '.'
1475 && pszLocation[1] == '/')
1476 pszLocation += 2;
1477
1478 /* Check if the file exists. */
1479 PVDFILE pIt = NULL;
1480 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1481 {
1482 if (!RTStrCmp(pIt->pszName, pszLocation))
1483 {
1484 fFound = true;
1485 break;
1486 }
1487 }
1488
1489 if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE)
1490 {
1491 /* If the file exists delete the memory disk. */
1492 if (fFound)
1493 rc = VDMemDiskSetSize(pIt->pMemDisk, 0);
1494 else
1495 {
1496 /* Create completey new. */
1497 pIt = (PVDFILE)RTMemAllocZ(sizeof(VDFILE));
1498 if (pIt)
1499 {
1500 pIt->pszName = RTStrDup(pszLocation);
1501
1502 if (pIt->pszName)
1503 {
1504 rc = VDMemDiskCreate(&pIt->pMemDisk, 0);
1505 }
1506 else
1507 rc = VERR_NO_MEMORY;
1508
1509 if (RT_FAILURE(rc))
1510 {
1511 if (pIt->pszName)
1512 RTStrFree(pIt->pszName);
1513 RTMemFree(pIt);
1514 }
1515 }
1516 else
1517 rc = VERR_NO_MEMORY;
1518
1519 RTListAppend(&pGlob->ListFiles, &pIt->Node);
1520 }
1521 }
1522 else if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN)
1523 {
1524 if (!fFound)
1525 rc = VERR_FILE_NOT_FOUND;
1526 }
1527 else
1528 rc = VERR_INVALID_PARAMETER;
1529
1530 if (RT_SUCCESS(rc))
1531 {
1532 AssertPtr(pIt);
1533 PVDSTORAGE pStorage = (PVDSTORAGE)RTMemAllocZ(sizeof(VDSTORAGE));
1534 if (!pStorage)
1535 rc = VERR_NO_MEMORY;
1536
1537 pStorage->pFile = pIt;
1538 pStorage->pfnComplete = pfnCompleted;
1539 *ppStorage = pStorage;
1540 }
1541
1542 return rc;
1543}
1544
1545static DECLCALLBACK(int) tstVDIoFileClose(void *pvUser, void *pStorage)
1546{
1547 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1548
1549 RTMemFree(pIoStorage);
1550 return VINF_SUCCESS;
1551}
1552
1553static DECLCALLBACK(int) tstVDIoFileDelete(void *pvUser, const char *pcszFilename)
1554{
1555 int rc = VINF_SUCCESS;
1556 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1557 bool fFound = false;
1558
1559 /*
1560 * Some backends use ./ for paths, strip it.
1561 * @todo: Implement proper directory support for the
1562 * memory filesystem.
1563 */
1564 if ( strlen(pcszFilename) >= 2
1565 && *pcszFilename == '.'
1566 && pcszFilename[1] == '/')
1567 pcszFilename += 2;
1568
1569 /* Check if the file exists. */
1570 PVDFILE pIt = NULL;
1571 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1572 {
1573 if (!RTStrCmp(pIt->pszName, pcszFilename))
1574 {
1575 fFound = true;
1576 break;
1577 }
1578 }
1579
1580 if (fFound)
1581 {
1582 RTListNodeRemove(&pIt->Node);
1583 VDMemDiskDestroy(pIt->pMemDisk);
1584 RTStrFree(pIt->pszName);
1585 RTMemFree(pIt);
1586 }
1587 else
1588 rc = VERR_FILE_NOT_FOUND;
1589
1590 return rc;
1591}
1592
1593static DECLCALLBACK(int) tstVDIoFileMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
1594{
1595 int rc = VINF_SUCCESS;
1596 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1597 bool fFound = false;
1598
1599 /* Check if the file exists. */
1600 PVDFILE pIt = NULL;
1601 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1602 {
1603 if (!RTStrCmp(pIt->pszName, pcszSrc))
1604 {
1605 fFound = true;
1606 break;
1607 }
1608 }
1609
1610 if (fFound)
1611 {
1612 char *pszNew = RTStrDup(pcszDst);
1613 if (pszNew)
1614 {
1615 RTStrFree(pIt->pszName);
1616 pIt->pszName = pszNew;
1617 }
1618 else
1619 rc = VERR_NO_MEMORY;
1620 }
1621 else
1622 rc = VERR_FILE_NOT_FOUND;
1623
1624 return rc;
1625}
1626
1627static DECLCALLBACK(int) tstVDIoFileGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
1628{
1629 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
1630
1631 *pcbFreeSpace = ~0ULL; /** @todo: Implement */
1632 return VINF_SUCCESS;
1633}
1634
1635static DECLCALLBACK(int) tstVDIoFileGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
1636{
1637 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
1638
1639 /** @todo: Implement */
1640 return VINF_SUCCESS;
1641}
1642
1643static DECLCALLBACK(int) tstVDIoFileGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
1644{
1645 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1646
1647 return VDMemDiskGetSize(pIoStorage->pFile->pMemDisk, pcbSize);
1648}
1649
1650static DECLCALLBACK(int) tstVDIoFileSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
1651{
1652 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1653
1654 return VDMemDiskSetSize(pIoStorage->pFile->pMemDisk, cbSize);
1655}
1656
1657static DECLCALLBACK(int) tstVDIoFileWriteSync(void *pvUser, void *pStorage, uint64_t uOffset,
1658 const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten)
1659{
1660 int rc = VINF_SUCCESS;
1661 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1662
1663 RTSGBUF SgBuf;
1664 RTSGSEG Seg;
1665
1666 Seg.pvSeg = (void *)pvBuffer;
1667 Seg.cbSeg = cbBuffer;
1668 RTSgBufInit(&SgBuf, &Seg, 1);
1669 rc = VDMemDiskWrite(pIoStorage->pFile->pMemDisk, uOffset, cbBuffer, &SgBuf);
1670 if (RT_SUCCESS(rc) && pcbWritten)
1671 *pcbWritten = cbBuffer;
1672
1673 return rc;
1674}
1675
1676static DECLCALLBACK(int) tstVDIoFileReadSync(void *pvUser, void *pStorage, uint64_t uOffset,
1677 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
1678{
1679 int rc = VINF_SUCCESS;
1680 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1681
1682 RTSGBUF SgBuf;
1683 RTSGSEG Seg;
1684
1685 Seg.pvSeg = pvBuffer;
1686 Seg.cbSeg = cbBuffer;
1687 RTSgBufInit(&SgBuf, &Seg, 1);
1688 rc = VDMemDiskRead(pIoStorage->pFile->pMemDisk, uOffset, cbBuffer, &SgBuf);
1689 if (RT_SUCCESS(rc) && pcbRead)
1690 *pcbRead = cbBuffer;
1691
1692 return rc;
1693}
1694
1695static DECLCALLBACK(int) tstVDIoFileFlushSync(void *pvUser, void *pStorage)
1696{
1697 /* nothing to do. */
1698 return VINF_SUCCESS;
1699}
1700
1701static DECLCALLBACK(int) tstVDIoFileReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
1702 PCRTSGSEG paSegments, size_t cSegments,
1703 size_t cbRead, void *pvCompletion,
1704 void **ppTask)
1705{
1706 int rc = VINF_SUCCESS;
1707 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1708 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1709
1710 rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_READ, uOffset,
1711 cbRead, paSegments, cSegments, pIoStorage->pfnComplete, pvCompletion);
1712 if (RT_SUCCESS(rc))
1713 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1714
1715 return rc;
1716}
1717
1718static DECLCALLBACK(int) tstVDIoFileWriteAsync(void *pvUser, void *pStorage, uint64_t uOffset,
1719 PCRTSGSEG paSegments, size_t cSegments,
1720 size_t cbWrite, void *pvCompletion,
1721 void **ppTask)
1722{
1723 int rc = VINF_SUCCESS;
1724 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1725 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1726
1727 rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_WRITE, uOffset,
1728 cbWrite, paSegments, cSegments, pIoStorage->pfnComplete, pvCompletion);
1729 if (RT_SUCCESS(rc))
1730 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1731
1732 return rc;
1733}
1734
1735static DECLCALLBACK(int) tstVDIoFileFlushAsync(void *pvUser, void *pStorage, void *pvCompletion,
1736 void **ppTask)
1737{
1738 int rc = VINF_SUCCESS;
1739 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1740 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1741
1742 rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_FLUSH, 0,
1743 0, NULL, 0, pIoStorage->pfnComplete, pvCompletion);
1744 if (RT_SUCCESS(rc))
1745 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1746
1747 return rc;
1748}
1749
1750static int tstVDIoTestInit(PVDIOTEST pIoTest, PVDTESTGLOB pGlob, bool fRandomAcc, uint64_t cbIo,
1751 size_t cbBlkSize, uint64_t offStart, uint64_t offEnd,
1752 unsigned uWriteChance)
1753{
1754 int rc = VINF_SUCCESS;
1755
1756 pIoTest->fRandomAccess = fRandomAcc;
1757 pIoTest->cbIo = cbIo;
1758 pIoTest->cbBlkIo = cbBlkSize;
1759 pIoTest->offStart = offStart;
1760 pIoTest->offEnd = offEnd;
1761 pIoTest->uWriteChance = uWriteChance;
1762 pIoTest->pIoRnd = pGlob->pIoRnd;
1763
1764 if (fRandomAcc)
1765 {
1766 uint64_t cbRange = pIoTest->offEnd < pIoTest->offStart
1767 ? pIoTest->offStart - pIoTest->offEnd
1768 : pIoTest->offEnd - pIoTest->offStart;
1769
1770 pIoTest->u.Rnd.cBlocks = cbRange / cbBlkSize + ((cbRange % cbBlkSize) ? 1 : 0);
1771 pIoTest->u.Rnd.cBlocksLeft = pIoTest->u.Rnd.cBlocks;
1772 pIoTest->u.Rnd.pbMapAccessed = (uint8_t *)RTMemAllocZ(pIoTest->u.Rnd.cBlocks / 8
1773 + ((pIoTest->u.Rnd.cBlocks % 8)
1774 ? 1
1775 : 0));
1776 if (!pIoTest->u.Rnd.pbMapAccessed)
1777 rc = VERR_NO_MEMORY;
1778 }
1779 else
1780 pIoTest->u.offNext = pIoTest->offEnd < pIoTest->offStart ? pIoTest->offStart - cbBlkSize : 0;
1781
1782 return rc;
1783}
1784
1785static void tstVDIoTestDestroy(PVDIOTEST pIoTest)
1786{
1787 if (pIoTest->fRandomAccess)
1788 RTMemFree(pIoTest->u.Rnd.pbMapAccessed);
1789}
1790
1791static bool tstVDIoTestRunning(PVDIOTEST pIoTest)
1792{
1793 return pIoTest->cbIo > 0;
1794}
1795
1796static bool tstVDIoTestReqOutstanding(PVDIOREQ pIoReq)
1797{
1798 return pIoReq->fOutstanding;
1799}
1800
1801/**
1802 * Returns true with the given chance in percent.
1803 *
1804 * @returns true or false
1805 * @param iPercentage The percentage of the chance to return true.
1806 */
1807static bool tstVDIoTestIsTrue(PVDIOTEST pIoTest, int iPercentage)
1808{
1809 int uRnd = VDIoRndGetU32Ex(pIoTest->pIoRnd, 0, 100);
1810
1811 return (uRnd <= iPercentage); /* This should be enough for our purpose */
1812}
1813
1814static int tstVDIoTestReqInit(PVDIOTEST pIoTest, PVDIOREQ pIoReq, void *pvUser)
1815{
1816 int rc = VINF_SUCCESS;
1817
1818 if (pIoTest->cbIo)
1819 {
1820 /* Read or Write? */
1821 pIoReq->enmTxDir = tstVDIoTestIsTrue(pIoTest, pIoTest->uWriteChance) ? VDIOREQTXDIR_WRITE : VDIOREQTXDIR_READ;
1822 pIoReq->cbReq = RT_MIN(pIoTest->cbBlkIo, pIoTest->cbIo);
1823 pIoTest->cbIo -= pIoReq->cbReq;
1824 pIoReq->DataSeg.cbSeg = pIoReq->cbReq;
1825
1826 if (pIoReq->enmTxDir == VDIOREQTXDIR_WRITE)
1827 {
1828 rc = VDIoRndGetBuffer(pIoTest->pIoRnd, &pIoReq->DataSeg.pvSeg, pIoReq->cbReq);
1829 AssertRC(rc);
1830 }
1831 else
1832 {
1833 /* Read */
1834 pIoReq->DataSeg.pvSeg = pIoReq->pvBufRead;
1835 }
1836
1837 if (RT_SUCCESS(rc))
1838 {
1839 RTSgBufInit(&pIoReq->SgBuf, &pIoReq->DataSeg, 1);
1840
1841 if (pIoTest->fRandomAccess)
1842 {
1843 int idx = -1;
1844
1845 idx = ASMBitFirstClear(pIoTest->u.Rnd.pbMapAccessed, pIoTest->u.Rnd.cBlocks);
1846
1847 /* In case this is the last request we don't need to search further. */
1848 if (pIoTest->u.Rnd.cBlocksLeft > 1)
1849 {
1850 int idxIo;
1851 idxIo = VDIoRndGetU32Ex(pIoTest->pIoRnd, idx, pIoTest->u.Rnd.cBlocks - 1);
1852
1853 /*
1854 * If the bit is marked free use it, otherwise search for the next free bit
1855 * and if that doesn't work use the first free bit.
1856 */
1857 if (ASMBitTest(pIoTest->u.Rnd.pbMapAccessed, idxIo))
1858 {
1859 idxIo = ASMBitNextClear(pIoTest->u.Rnd.pbMapAccessed, pIoTest->u.Rnd.cBlocks, idxIo);
1860 if (idxIo != -1)
1861 idx = idxIo;
1862 }
1863 else
1864 idx = idxIo;
1865 }
1866
1867 Assert(idx != -1);
1868 pIoReq->off = idx * pIoTest->cbBlkIo;
1869 pIoTest->u.Rnd.cBlocksLeft--;
1870 if (!pIoTest->u.Rnd.cBlocksLeft)
1871 {
1872 /* New round, clear everything. */
1873 ASMBitClearRange(pIoTest->u.Rnd.pbMapAccessed, 0, pIoTest->u.Rnd.cBlocks);
1874 pIoTest->u.Rnd.cBlocksLeft = pIoTest->u.Rnd.cBlocks;
1875 }
1876 else
1877 ASMBitSet(pIoTest->u.Rnd.pbMapAccessed, idx);
1878 }
1879 else
1880 {
1881 pIoReq->off = pIoTest->u.offNext;
1882 if (pIoTest->offEnd < pIoTest->offStart)
1883 {
1884 pIoTest->u.offNext = pIoTest->u.offNext == 0
1885 ? pIoTest->offEnd - pIoTest->cbBlkIo
1886 : RT_MAX(pIoTest->offEnd, pIoTest->u.offNext - pIoTest->cbBlkIo);
1887 }
1888 else
1889 {
1890 pIoTest->u.offNext = pIoTest->u.offNext + pIoTest->cbBlkIo >= pIoTest->offEnd
1891 ? 0
1892 : RT_MIN(pIoTest->offEnd, pIoTest->u.offNext + pIoTest->cbBlkIo);
1893 }
1894 }
1895 pIoReq->pvUser = pvUser;
1896 pIoReq->fOutstanding = true;
1897 }
1898 }
1899 else
1900 rc = VERR_ACCESS_DENIED;
1901
1902 return rc;
1903}
1904
1905static void tstVDIoTestReqComplete(void *pvUser1, void *pvUser2, int rcReq)
1906{
1907 PVDIOREQ pIoReq = (PVDIOREQ)pvUser1;
1908 RTSEMEVENT hEventSem = (RTSEMEVENT)pvUser2;
1909 PVDDISK pDisk = (PVDDISK)pIoReq->pvUser;
1910
1911 if (pDisk->pMemDiskVerify)
1912 {
1913 switch (pIoReq->enmTxDir)
1914 {
1915 case VDIOREQTXDIR_READ:
1916 {
1917 RTCritSectEnter(&pDisk->CritSectVerify);
1918 RTSgBufReset(&pIoReq->SgBuf);
1919
1920 if (VDMemDiskCmp(pDisk->pMemDiskVerify, pIoReq->off, pIoReq->cbReq,
1921 &pIoReq->SgBuf))
1922 RTPrintf("Corrupted disk at offset %llu!\n", pIoReq->off);
1923 RTCritSectLeave(&pDisk->CritSectVerify);
1924 }
1925 case VDIOREQTXDIR_WRITE:
1926 {
1927 RTCritSectEnter(&pDisk->CritSectVerify);
1928 RTSgBufReset(&pIoReq->SgBuf);
1929
1930 int rc = VDMemDiskWrite(pDisk->pMemDiskVerify, pIoReq->off, pIoReq->cbReq,
1931 &pIoReq->SgBuf);
1932 AssertRC(rc);
1933 RTCritSectLeave(&pDisk->CritSectVerify);
1934 break;
1935 }
1936 case VDIOREQTXDIR_FLUSH:
1937 break;
1938 }
1939 }
1940
1941 ASMAtomicXchgBool(&pIoReq->fOutstanding, false);
1942 RTSemEventSignal(hEventSem);
1943 return;
1944}
1945
1946/**
1947 * Returns the disk handle by name or NULL if not found
1948 *
1949 * @returns Disk handle or NULL if the disk could not be found.
1950 *
1951 * @param pGlob Global test state.
1952 * @param pcszDisk Name of the disk to get.
1953 */
1954static PVDDISK tstVDIoGetDiskByName(PVDTESTGLOB pGlob, const char *pcszDisk)
1955{
1956 PVDDISK pIt = NULL;
1957 bool fFound = false;
1958
1959 LogFlowFunc(("pGlob=%#p pcszDisk=%s\n", pGlob, pcszDisk));
1960
1961 RTListForEach(&pGlob->ListDisks, pIt, VDDISK, ListNode)
1962 {
1963 if (!RTStrCmp(pIt->pszName, pcszDisk))
1964 {
1965 fFound = true;
1966 break;
1967 }
1968 }
1969
1970 LogFlowFunc(("return %#p\n", fFound ? pIt : NULL));
1971 return fFound ? pIt : NULL;
1972}
1973
1974/**
1975 * Skips the characters until the given character is reached.
1976 *
1977 * @returns Start of the string with the given character
1978 * or NULL if the string ended before.
1979 *
1980 * @param psz The string to skip.
1981 * @param ch The character.
1982 */
1983static char *tstVDIoScriptSkipUntil(char *psz, char ch)
1984{
1985 while ( *psz != '\0'
1986 && *psz != ch)
1987 psz++;
1988
1989 return psz;
1990}
1991
1992/**
1993 * Skips the spaces of the current string.
1994 *
1995 * @returns Start of the string with a non space character
1996 * or NULL if the string ended before.
1997 *
1998 * @param psz The string to skip.
1999 */
2000static char *tstVDIoScriptSkipSpace(char *psz)
2001{
2002 while ( *psz != '\0'
2003 && RT_C_IS_SPACE(*psz))
2004 psz++;
2005
2006 return psz;
2007}
2008
2009/**
2010 * Skips all characters until a space is reached of the current
2011 * string.
2012 *
2013 * @returns Start of the string with a space character
2014 * or NULL if the string ended before.
2015 *
2016 * @param psz The string to skip.
2017 */
2018static char *tstVDIoScriptSkipNonSpace(char *psz)
2019{
2020 while ( *psz != '\0'
2021 && !RT_C_IS_SPACE(*psz))
2022 psz++;
2023
2024 return psz;
2025}
2026
2027/**
2028 * Returns true if the first character of the given string
2029 * contains a character marking a line end (comment or \0
2030 * terminator).
2031 *
2032 * @returns true if the line contains no more characters of
2033 * interest and false otherwise.
2034 *
2035 * @param psz The string to check for.
2036 */
2037static bool tstVDIoIsLineEnd(const char *psz)
2038{
2039 return *psz == '\0' || *psz == '#';
2040}
2041
2042/**
2043 * Parses one argument name, value pair.
2044 *
2045 * @returns IPRT status code.
2046 *
2047 * @param pVDScriptAction Script action.
2048 * @param pcszName Argument name.
2049 * @param pcszValue Argument value.
2050 * @param pScriptArg Where to fill in the parsed
2051 * argument.
2052 * @param pfMandatory Where to store whether the argument
2053 * is mandatory.
2054 */
2055static int tstVDIoScriptArgumentParse(PCVDSCRIPTACTION pVDScriptAction, const char *pcszName,
2056 const char *pcszValue, PVDSCRIPTARG pScriptArg, bool *pfMandatory)
2057{
2058 int rc = VERR_NOT_FOUND;
2059
2060 for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
2061 {
2062 if (!RTStrCmp(pVDScriptAction->paArgDesc[i].pcszName, pcszName))
2063 {
2064 rc = VINF_SUCCESS;
2065
2066 switch (pVDScriptAction->paArgDesc[i].enmType)
2067 {
2068 case VDSCRIPTARGTYPE_BOOL:
2069 {
2070 pScriptArg->enmType = VDSCRIPTARGTYPE_BOOL;
2071 if (!RTStrICmp(pcszValue, "yes") || !RTStrICmp(pcszValue, "on"))
2072 pScriptArg->u.fFlag = true;
2073 else if (!RTStrICmp(pcszValue, "no") || !RTStrICmp(pcszValue, "off"))
2074 pScriptArg->u.fFlag = false;
2075 else
2076 {
2077 RTPrintf("Boolean argument malformed '%s'\n", pcszValue);
2078 rc = VERR_INVALID_PARAMETER;
2079 }
2080 break;
2081 }
2082 case VDSCRIPTARGTYPE_SIGNED_NUMBER:
2083 {
2084 pScriptArg->enmType = VDSCRIPTARGTYPE_SIGNED_NUMBER;
2085 AssertMsgFailed(("todo\n"));
2086 break;
2087 }
2088 case VDSCRIPTARGTYPE_STRING:
2089 {
2090 pScriptArg->enmType = VDSCRIPTARGTYPE_STRING;
2091 pScriptArg->u.pcszString = pcszValue;
2092 break;
2093 }
2094 case VDSCRIPTARGTYPE_UNSIGNED_NUMBER:
2095 {
2096 char *pszSuffix = NULL;
2097
2098 pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_NUMBER;
2099 rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.u64);
2100 if (rc == VWRN_TRAILING_CHARS)
2101 {
2102 switch (*pszSuffix)
2103 {
2104 case 'k':
2105 case 'K':
2106 {
2107 pScriptArg->u.u64 *= _1K;
2108 break;
2109 }
2110 case 'm':
2111 case 'M':
2112 {
2113 pScriptArg->u.u64 *= _1M;
2114 break;
2115 }
2116 case 'g':
2117 case 'G':
2118 {
2119 pScriptArg->u.u64 *= _1G;
2120 break;
2121 }
2122 default:
2123 {
2124 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
2125 rc = VERR_INVALID_PARAMETER;
2126 }
2127 }
2128 if (rc != VERR_INVALID_PARAMETER)
2129 rc = VINF_SUCCESS;
2130 }
2131
2132 break;
2133 }
2134 case VDSCRIPTARGTYPE_UNSIGNED_RANGE:
2135 {
2136 char *pszSuffix = NULL;
2137
2138 pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_RANGE;
2139 rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.Range.Start);
2140 if (rc == VWRN_TRAILING_CHARS)
2141 {
2142 if (*pszSuffix != '-')
2143 {
2144 switch (*pszSuffix)
2145 {
2146 case 'k':
2147 case 'K':
2148 {
2149 pScriptArg->u.u64 *= _1K;
2150 break;
2151 }
2152 case 'm':
2153 case 'M':
2154 {
2155 pScriptArg->u.u64 *= _1M;
2156 break;
2157 }
2158 case 'g':
2159 case 'G':
2160 {
2161 pScriptArg->u.u64 *= _1G;
2162 break;
2163 }
2164 default:
2165 {
2166 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
2167 rc = VERR_INVALID_PARAMETER;
2168 }
2169 }
2170 if (RT_SUCCESS(rc))
2171 pszSuffix++;
2172 }
2173
2174 if (*pszSuffix == '-')
2175 {
2176 pszSuffix++;
2177 rc = RTStrToUInt64Ex(pszSuffix, &pszSuffix, 10, &pScriptArg->u.Range.End);
2178 if (rc == VWRN_TRAILING_CHARS)
2179 {
2180 switch (*pszSuffix)
2181 {
2182 case 'k':
2183 case 'K':
2184 {
2185 pScriptArg->u.Range.End *= _1K;
2186 break;
2187 }
2188 case 'm':
2189 case 'M':
2190 {
2191 pScriptArg->u.Range.End *= _1M;
2192 break;
2193 }
2194 case 'g':
2195 case 'G':
2196 {
2197 pScriptArg->u.Range.End *= _1G;
2198 break;
2199 }
2200 default:
2201 {
2202 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
2203 rc = VERR_INVALID_PARAMETER;
2204 }
2205 }
2206 }
2207 }
2208 else
2209 rc = VERR_INVALID_PARAMETER;
2210 }
2211 else
2212 rc = VERR_INVALID_PARAMETER;
2213
2214 if (rc == VERR_INVALID_PARAMETER)
2215 RTPrintf("Invalid range format\n");
2216 break;
2217 }
2218 default:
2219 AssertMsgFailed(("Invalid script argument type\n"));
2220 }
2221
2222 if (RT_SUCCESS(rc))
2223 {
2224 pScriptArg->chId = pVDScriptAction->paArgDesc[i].chId;
2225 *pfMandatory = !!(pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY);
2226 }
2227 break;
2228 }
2229 }
2230
2231 if (rc == VERR_NOT_FOUND)
2232 RTPrintf("Argument '%s' not found\n", pcszName);
2233
2234 return rc;
2235}
2236
2237/**
2238 * Parses the arguments of a action in the script.
2239 *
2240 * @returns IPRT status code.
2241 *
2242 * @param psz Argument string.
2243 * @param pVDScriptAction The script action to parses
2244 * arguments for.
2245 * @param paScriptArgs Where to store the arguments.
2246 * @param pcScriptArgs Where to store the actual number of
2247 * arguments parsed.
2248 */
2249static int tstVDIoScriptArgumentListParse(char *psz, PCVDSCRIPTACTION pVDScriptAction, PVDSCRIPTARG paScriptArgs, unsigned *pcScriptArgs)
2250{
2251 int rc = VINF_SUCCESS;
2252 unsigned cMandatoryArgsReq = 0;
2253 unsigned cScriptArgs = 0;
2254
2255 /* Count the number of mandatory arguments first. */
2256 for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
2257 if (pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY)
2258 cMandatoryArgsReq++;
2259
2260 /* One argument is given in the form name=value. */
2261 *pcScriptArgs = 0;
2262
2263 while ( psz
2264 && !tstVDIoIsLineEnd(psz))
2265 {
2266 const char *pcszName = psz;
2267
2268 psz = tstVDIoScriptSkipUntil(psz, '=');
2269 if (!tstVDIoIsLineEnd(psz))
2270 {
2271 *psz = '\0'; /* Overwrite */
2272 psz++;
2273 const char *pcszValue = psz;
2274
2275 psz = tstVDIoScriptSkipNonSpace(psz);
2276 if (!tstVDIoIsLineEnd(psz))
2277 {
2278 *psz = '\0'; /* Overwrite */
2279 psz++;
2280 psz = tstVDIoScriptSkipSpace(psz);
2281 }
2282
2283 pcszValue = tstVDIoScriptSkipSpace((char *)pcszValue);
2284 if (*pcszValue == '\0')
2285 {
2286 RTPrintf("Value missing for argument '%s'\n", pcszName);
2287 rc = VERR_INVALID_STATE;
2288 break;
2289 }
2290
2291 /* We have the name and value pair now. */
2292 bool fMandatory = false; /* Shut up gcc */
2293 rc = tstVDIoScriptArgumentParse(pVDScriptAction, pcszName, pcszValue, &paScriptArgs[cScriptArgs], &fMandatory);
2294 if (RT_SUCCESS(rc))
2295 {
2296 if (fMandatory)
2297 cMandatoryArgsReq--;
2298 cScriptArgs++;
2299 }
2300 }
2301 else
2302 {
2303 RTPrintf("Argument in invalid form\n");
2304 rc = VERR_INVALID_STATE;
2305 break;
2306 }
2307 }
2308
2309 if ( RT_SUCCESS(rc)
2310 && cMandatoryArgsReq)
2311 {
2312 /* No arguments anymore but there are still mandatory arguments left. */
2313 RTPrintf("There are %u arguments missing for script action '%s\n", pVDScriptAction->pcszAction);
2314 rc = VERR_INVALID_STATE;
2315 }
2316
2317 if (RT_SUCCESS(rc))
2318 *pcScriptArgs = cScriptArgs;
2319
2320 return rc;
2321}
2322
2323/**
2324 * Executes the script pointed to by the given stream.
2325 *
2326 * @returns IPRT status code.
2327 *
2328 * @param pStrm The stream handle of the script.
2329 * @param pGlob Global test data.
2330 */
2331static int tstVDIoScriptExecute(PRTSTREAM pStrm, PVDTESTGLOB pGlob)
2332{
2333 int rc = VINF_SUCCESS;
2334 char abBuffer[0x1000]; /* Current assumption that a line is never longer than 4096 bytes. */
2335 PVDSCRIPTARG paScriptArgs = NULL;
2336 unsigned cScriptArgsMax = 0;
2337
2338 do
2339 {
2340 memset(abBuffer, 0, sizeof(abBuffer));
2341 rc = RTStrmGetLine(pStrm, abBuffer, sizeof(abBuffer));
2342 if (RT_SUCCESS(rc))
2343 {
2344 const char *pcszAction = NULL;
2345 char *psz = abBuffer;
2346
2347 /* Skip space */
2348 psz = tstVDIoScriptSkipSpace(psz);
2349 if (!tstVDIoIsLineEnd(psz))
2350 {
2351 PCVDSCRIPTACTION pVDScriptAction = NULL;
2352
2353 /* Get the action name. */
2354 pcszAction = psz;
2355
2356 psz = tstVDIoScriptSkipNonSpace(psz);
2357 if (!tstVDIoIsLineEnd(psz))
2358 {
2359 Assert(RT_C_IS_SPACE(*psz));
2360 *psz++ = '\0';
2361 }
2362
2363 /* Find the action. */
2364 for (unsigned i = 0; i < g_cScriptActions; i++)
2365 {
2366 if (!RTStrCmp(pcszAction, g_aScriptActions[i].pcszAction))
2367 {
2368 pVDScriptAction = &g_aScriptActions[i];
2369 break;
2370 }
2371 }
2372
2373 if (pVDScriptAction)
2374 {
2375 /* Parse arguments. */
2376 if (cScriptArgsMax < pVDScriptAction->cArgDescs)
2377 {
2378 /* Increase arguments array. */
2379 if (paScriptArgs)
2380 RTMemFree(paScriptArgs);
2381
2382 cScriptArgsMax = pVDScriptAction->cArgDescs;
2383 paScriptArgs = (PVDSCRIPTARG)RTMemAllocZ(cScriptArgsMax * sizeof(VDSCRIPTARG));
2384 }
2385
2386 if (paScriptArgs)
2387 {
2388 unsigned cScriptArgs;
2389
2390 rc = tstVDIoScriptArgumentListParse(psz, pVDScriptAction, paScriptArgs, &cScriptArgs);
2391 if (RT_SUCCESS(rc))
2392 {
2393 /* Execute the handler. */
2394 rc = pVDScriptAction->pfnHandler(pGlob, paScriptArgs, cScriptArgs);
2395 }
2396 }
2397 else
2398 {
2399 RTPrintf("Out of memory while allocating argument array for script action %s\n", pcszAction);
2400 rc = VERR_NO_MEMORY;
2401 }
2402 }
2403 else
2404 {
2405 RTPrintf("Script action %s is not known\n", pcszAction);
2406 rc = VERR_NOT_FOUND;
2407 }
2408 }
2409 /* else empty line, just continue */
2410 }
2411 } while(RT_SUCCESS(rc));
2412
2413 if (rc == VERR_EOF)
2414 {
2415 RTPrintf("Successfully executed I/O script\n");
2416 rc = VINF_SUCCESS;
2417 }
2418 return rc;
2419}
2420
2421/**
2422 * Executes the given I/O script.
2423 *
2424 * @returns nothing.
2425 *
2426 * @param pcszFilename The script to execute.
2427 */
2428static void tstVDIoScriptRun(const char *pcszFilename)
2429{
2430 int rc = VINF_SUCCESS;
2431 PRTSTREAM pScriptStrm; /**< Stream of the script file. */
2432 VDTESTGLOB GlobTest; /**< Global test data. */
2433
2434 memset(&GlobTest, 0, sizeof(VDTESTGLOB));
2435 RTListInit(&GlobTest.ListFiles);
2436 RTListInit(&GlobTest.ListDisks);
2437
2438 rc = RTStrmOpen(pcszFilename, "r", &pScriptStrm);
2439 if (RT_SUCCESS(rc))
2440 {
2441 /* Init global test data. */
2442 GlobTest.VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
2443 GlobTest.VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
2444 GlobTest.VDIErrorCallbacks.pfnError = tstVDError;
2445 GlobTest.VDIErrorCallbacks.pfnMessage = tstVDMessage;
2446
2447 rc = VDInterfaceAdd(&GlobTest.VDIError, "tstVDIo_VDIError", VDINTERFACETYPE_ERROR,
2448 &GlobTest.VDIErrorCallbacks, NULL, &GlobTest.pInterfacesDisk);
2449 AssertRC(rc);
2450
2451 GlobTest.VDIIoCallbacks.cbSize = sizeof(VDINTERFACEIO);
2452 GlobTest.VDIIoCallbacks.enmInterface = VDINTERFACETYPE_IO;
2453 GlobTest.VDIIoCallbacks.pfnOpen = tstVDIoFileOpen;
2454 GlobTest.VDIIoCallbacks.pfnClose = tstVDIoFileClose;
2455 GlobTest.VDIIoCallbacks.pfnDelete = tstVDIoFileDelete;
2456 GlobTest.VDIIoCallbacks.pfnMove = tstVDIoFileMove;
2457 GlobTest.VDIIoCallbacks.pfnGetFreeSpace = tstVDIoFileGetFreeSpace;
2458 GlobTest.VDIIoCallbacks.pfnGetModificationTime = tstVDIoFileGetModificationTime;
2459 GlobTest.VDIIoCallbacks.pfnGetSize = tstVDIoFileGetSize;
2460 GlobTest.VDIIoCallbacks.pfnSetSize = tstVDIoFileSetSize;
2461 GlobTest.VDIIoCallbacks.pfnWriteSync = tstVDIoFileWriteSync;
2462 GlobTest.VDIIoCallbacks.pfnReadSync = tstVDIoFileReadSync;
2463 GlobTest.VDIIoCallbacks.pfnFlushSync = tstVDIoFileFlushSync;
2464 GlobTest.VDIIoCallbacks.pfnReadAsync = tstVDIoFileReadAsync;
2465 GlobTest.VDIIoCallbacks.pfnWriteAsync = tstVDIoFileWriteAsync;
2466 GlobTest.VDIIoCallbacks.pfnFlushAsync = tstVDIoFileFlushAsync;
2467
2468 rc = VDInterfaceAdd(&GlobTest.VDIIo, "tstVDIo_VDIIo", VDINTERFACETYPE_IO,
2469 &GlobTest.VDIIoCallbacks, &GlobTest, &GlobTest.pInterfacesImages);
2470 AssertRC(rc);
2471
2472 /* Init I/O backend. */
2473 rc = VDIoBackendMemCreate(&GlobTest.pIoBackend);
2474 if (RT_SUCCESS(rc))
2475 {
2476 /* Execute the script. */
2477 rc = tstVDIoScriptExecute(pScriptStrm, &GlobTest);
2478 if (RT_FAILURE(rc))
2479 {
2480 RTPrintf("Executing the script stream failed rc=%Rrc\n", rc);
2481 }
2482 VDIoBackendMemDestroy(GlobTest.pIoBackend);
2483 }
2484 else
2485 RTPrintf("Creating the I/O backend failed rc=%Rrc\n");
2486
2487 RTStrmClose(pScriptStrm);
2488 }
2489 else
2490 RTPrintf("Opening script failed rc=%Rrc\n", rc);
2491}
2492
2493/**
2494 * Shows help message.
2495 */
2496static void printUsage(void)
2497{
2498 RTPrintf("Usage:\n"
2499 "--script <filename> Script to execute\n"
2500 "--replay <filename> Log to replay (not implemented yet)\n");
2501}
2502
2503static const RTGETOPTDEF g_aOptions[] =
2504{
2505 { "--script", 's', RTGETOPT_REQ_STRING },
2506 { "--replay", 'r', RTGETOPT_REQ_STRING },
2507};
2508
2509int main(int argc, char *argv[])
2510{
2511 RTR3Init();
2512 int rc;
2513 RTGETOPTUNION ValueUnion;
2514 RTGETOPTSTATE GetState;
2515 char c;
2516
2517 if (argc != 3)
2518 {
2519 printUsage();
2520 return RTEXITCODE_FAILURE;
2521 }
2522
2523 rc = VDInit();
2524 if (RT_FAILURE(rc))
2525 return RTEXITCODE_FAILURE;
2526
2527 RTGetOptInit(&GetState, argc, argv, g_aOptions,
2528 RT_ELEMENTS(g_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
2529
2530 while ( RT_SUCCESS(rc)
2531 && (c = RTGetOpt(&GetState, &ValueUnion)))
2532 {
2533 switch (c)
2534 {
2535 case 's':
2536 tstVDIoScriptRun(ValueUnion.psz);
2537 break;
2538 case 'r':
2539 RTPrintf("Replaying I/O logs is not implemented yet\n");
2540 break;
2541 default:
2542 printUsage();
2543 }
2544 }
2545
2546 rc = VDShutdown();
2547 if (RT_FAILURE(rc))
2548 RTPrintf("tstVDIo: unloading backends failed! rc=%Rrc\n", rc);
2549
2550 return RTEXITCODE_SUCCESS;
2551}
2552
Note: See TracBrowser for help on using the repository browser.

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