VirtualBox

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

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

tstVDIo: Two new handlers to test VDMerge and VDCompact

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

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