VirtualBox

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

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

tstVDIo: Implement support for async flushes

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

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