VirtualBox

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

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

tstVDIo: Updates

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