VirtualBox

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

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

tstVDIo: Fix the testcase

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 95.5 KB
Line 
1/* $Id: tstVDIo.cpp 36949 2011-05-03 21:38:57Z 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 /** @todo */
1065 rc = VERR_NOT_IMPLEMENTED;
1066 }
1067 else
1068 rc = VDFlush(pDisk->pVD);
1069 }
1070
1071 return rc;
1072}
1073
1074static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1075{
1076 int rc = VINF_SUCCESS;
1077 const char *pcszDisk = NULL;
1078 PVDDISK pDisk = NULL;
1079 unsigned nImageFrom = 0;
1080 unsigned nImageTo = 0;
1081
1082 for (unsigned i = 0; i < cScriptArgs; i++)
1083 {
1084 switch (paScriptArgs[i].chId)
1085 {
1086 case 'd':
1087 {
1088 pcszDisk = paScriptArgs[i].u.pcszString;
1089 break;
1090 }
1091 case 'f':
1092 {
1093 nImageFrom = (unsigned)paScriptArgs[i].u.u64;
1094 break;
1095 }
1096 case 't':
1097 {
1098 nImageTo = (unsigned)paScriptArgs[i].u.u64;
1099 break;
1100 }
1101
1102 default:
1103 AssertMsgFailed(("Invalid argument given!\n"));
1104 }
1105
1106 if (RT_FAILURE(rc))
1107 break;
1108 }
1109
1110 if (RT_SUCCESS(rc))
1111 {
1112 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1113 if (!pDisk)
1114 rc = VERR_NOT_FOUND;
1115 else
1116 {
1117 /** @todo: Provide progress interface to test that cancelation
1118 * doesn't corrupt the data.
1119 */
1120 rc = VDMerge(pDisk->pVD, nImageFrom, nImageTo, NULL);
1121 }
1122 }
1123
1124 return rc;
1125}
1126
1127static DECLCALLBACK(int) vdScriptHandlerCompact(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1128{
1129 int rc = VINF_SUCCESS;
1130 const char *pcszDisk = NULL;
1131 PVDDISK pDisk = NULL;
1132 unsigned nImage = 0;
1133
1134 for (unsigned i = 0; i < cScriptArgs; i++)
1135 {
1136 switch (paScriptArgs[i].chId)
1137 {
1138 case 'd':
1139 {
1140 pcszDisk = paScriptArgs[i].u.pcszString;
1141 break;
1142 }
1143 case 'i':
1144 {
1145 nImage = (unsigned)paScriptArgs[i].u.u64;
1146 break;
1147 }
1148
1149 default:
1150 AssertMsgFailed(("Invalid argument given!\n"));
1151 }
1152
1153 if (RT_FAILURE(rc))
1154 break;
1155 }
1156
1157 if (RT_SUCCESS(rc))
1158 {
1159 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1160 if (!pDisk)
1161 rc = VERR_NOT_FOUND;
1162 else
1163 {
1164 /** @todo: Provide progress interface to test that cancelation
1165 * doesn't corrupt the data.
1166 */
1167 rc = VDCompact(pDisk->pVD, nImage, NULL);
1168 }
1169 }
1170
1171 return rc;
1172}
1173
1174static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1175{
1176 int rc = VINF_SUCCESS;
1177 bool fAll = false;
1178 bool fDelete = false;
1179 const char *pcszDisk = NULL;
1180 PVDDISK pDisk = NULL;
1181
1182 for (unsigned i = 0; i < cScriptArgs; i++)
1183 {
1184 switch (paScriptArgs[i].chId)
1185 {
1186 case 'd':
1187 {
1188 pcszDisk = paScriptArgs[i].u.pcszString;
1189 break;
1190 }
1191 case 'm':
1192 {
1193 if (!RTStrICmp(paScriptArgs[i].u.pcszString, "all"))
1194 fAll = true;
1195 else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "single"))
1196 fAll = false;
1197 else
1198 {
1199 RTPrintf("Invalid mode '%s' given\n", paScriptArgs[i].u.pcszString);
1200 rc = VERR_INVALID_PARAMETER;
1201 }
1202 break;
1203 }
1204 case 'r':
1205 {
1206 fDelete = paScriptArgs[i].u.fFlag;
1207 break;
1208 }
1209 default:
1210 AssertMsgFailed(("Invalid argument given!\n"));
1211 }
1212
1213 if (RT_FAILURE(rc))
1214 break;
1215 }
1216
1217 if ( RT_SUCCESS(rc)
1218 && fAll
1219 && fDelete)
1220 {
1221 RTPrintf("mode=all doesn't work with delete=yes\n");
1222 rc = VERR_INVALID_PARAMETER;
1223 }
1224
1225 if (RT_SUCCESS(rc))
1226 {
1227 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1228 if (pDisk)
1229 {
1230 if (fAll)
1231 rc = VDCloseAll(pDisk->pVD);
1232 else
1233 rc = VDClose(pDisk->pVD, fDelete);
1234 }
1235 else
1236 rc = VERR_NOT_FOUND;
1237 }
1238 return rc;
1239}
1240
1241
1242static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1243{
1244 int rc = VINF_SUCCESS;
1245 size_t cbPattern = 0;
1246 uint64_t uSeed = 0;
1247 const char *pcszSeeder = NULL;
1248
1249 for (unsigned i = 0; i < cScriptArgs; i++)
1250 {
1251 switch (paScriptArgs[i].chId)
1252 {
1253 case 'd':
1254 {
1255 cbPattern = paScriptArgs[i].u.u64;
1256 break;
1257 }
1258 case 's':
1259 {
1260 uSeed = paScriptArgs[i].u.u64;
1261 break;
1262 }
1263 case 'm':
1264 {
1265 pcszSeeder = paScriptArgs[i].u.pcszString;
1266 break;
1267 }
1268 default:
1269 AssertMsgFailed(("Invalid argument given!\n"));
1270 }
1271 }
1272
1273 if (pGlob->pIoRnd)
1274 {
1275 RTPrintf("I/O RNG already exists\n");
1276 rc = VERR_INVALID_STATE;
1277 }
1278 else
1279 {
1280 uint64_t uSeedToUse = 0;
1281
1282 if (!RTStrICmp(pcszSeeder, "manual"))
1283 uSeedToUse = uSeed;
1284 else if (!RTStrICmp(pcszSeeder, "time"))
1285 uSeedToUse = RTTimeSystemMilliTS();
1286 else if (!RTStrICmp(pcszSeeder, "system"))
1287 {
1288 RTRAND hRand;
1289 rc = RTRandAdvCreateSystemTruer(&hRand);
1290 if (RT_SUCCESS(rc))
1291 {
1292 RTRandAdvBytes(hRand, &uSeedToUse, sizeof(uSeedToUse));
1293 RTRandAdvDestroy(hRand);
1294 }
1295 }
1296
1297 if (RT_SUCCESS(rc))
1298 rc = VDIoRndCreate(&pGlob->pIoRnd, cbPattern, uSeed);
1299 }
1300
1301 return rc;
1302}
1303
1304static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1305{
1306 if (pGlob->pIoRnd)
1307 {
1308 VDIoRndDestroy(pGlob->pIoRnd);
1309 pGlob->pIoRnd = NULL;
1310 }
1311 else
1312 RTPrintf("WARNING: No I/O RNG active, faulty script. Continuing\n");
1313
1314 return VINF_SUCCESS;
1315}
1316
1317static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1318{
1319 int rc = VINF_SUCCESS;
1320 size_t cbPattern = 0;
1321 const char *pcszName = NULL;
1322 uint64_t u64Pattern = 0;
1323
1324 for (unsigned i = 0; i < cScriptArgs; i++)
1325 {
1326 switch (paScriptArgs[i].chId)
1327 {
1328 case 'n':
1329 {
1330 pcszName = paScriptArgs[i].u.pcszString;
1331 break;
1332 }
1333 case 's':
1334 {
1335 cbPattern = paScriptArgs[i].u.u64;
1336 break;
1337 }
1338 case 'p':
1339 {
1340 u64Pattern = paScriptArgs[i].u.u64;
1341 break;
1342 }
1343 default:
1344 AssertMsgFailed(("Invalid argument given!\n"));
1345 }
1346 }
1347
1348 PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName);
1349 if (!pPattern)
1350 {
1351 pPattern = tstVDIoPatternCreate(pcszName, RT_ALIGN_Z(cbPattern, sizeof(uint64_t)));
1352 if (pPattern)
1353 {
1354 /* Fill the buffer. */
1355 void *pv = pPattern->pvPattern;
1356
1357 while (pPattern->cbPattern > 0)
1358 {
1359 *((uint64_t*)pv) = u64Pattern;
1360 pPattern->cbPattern -= sizeof(uint64_t);
1361 pv = (uint64_t *)pv + 1;
1362 }
1363 pPattern->cbPattern = cbPattern; /* Set to the desired size. (could be unaligned) */
1364
1365 RTListAppend(&pGlob->ListPatterns, &pPattern->ListNode);
1366 }
1367 else
1368 rc = VERR_NO_MEMORY;
1369 }
1370 else
1371 rc = VERR_ALREADY_EXISTS;
1372
1373 return rc;
1374}
1375
1376static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1377{
1378 int rc = VINF_SUCCESS;
1379 const char *pcszName = NULL;
1380 const char *pcszFile = NULL;
1381
1382 for (unsigned i = 0; i < cScriptArgs; i++)
1383 {
1384 switch (paScriptArgs[i].chId)
1385 {
1386 case 'n':
1387 {
1388 pcszName = paScriptArgs[i].u.pcszString;
1389 break;
1390 }
1391 case 'f':
1392 {
1393 pcszFile = paScriptArgs[i].u.pcszString;
1394 break;
1395 }
1396 default:
1397 AssertMsgFailed(("Invalid argument given!\n"));
1398 }
1399 }
1400
1401 PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName);
1402 if (!pPattern)
1403 {
1404 RTFILE hFile;
1405 uint64_t cbPattern = 0;
1406
1407 rc = RTFileOpen(&hFile, pcszFile, RTFILE_O_DENY_NONE | RTFILE_O_OPEN | RTFILE_O_READ);
1408 if (RT_SUCCESS(rc))
1409 {
1410 rc = RTFileGetSize(hFile, &cbPattern);
1411 if (RT_SUCCESS(rc))
1412 {
1413 pPattern = tstVDIoPatternCreate(pcszName, (size_t)cbPattern);
1414 if (pPattern)
1415 {
1416 rc = RTFileRead(hFile, pPattern->pvPattern, (size_t)cbPattern, NULL);
1417 if (RT_SUCCESS(rc))
1418 RTListAppend(&pGlob->ListPatterns, &pPattern->ListNode);
1419 else
1420 {
1421 RTMemFree(pPattern->pvPattern);
1422 RTStrFree(pPattern->pszName);
1423 RTMemFree(pPattern);
1424 }
1425 }
1426 else
1427 rc = VERR_NO_MEMORY;
1428 }
1429 RTFileClose(hFile);
1430 }
1431 }
1432 else
1433 rc = VERR_ALREADY_EXISTS;
1434
1435 return rc;
1436}
1437
1438static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1439{
1440 int rc = VINF_SUCCESS;
1441 const char *pcszName = NULL;
1442
1443 for (unsigned i = 0; i < cScriptArgs; i++)
1444 {
1445 switch (paScriptArgs[i].chId)
1446 {
1447 case 'n':
1448 {
1449 pcszName = paScriptArgs[i].u.pcszString;
1450 break;
1451 }
1452 default:
1453 AssertMsgFailed(("Invalid argument given!\n"));
1454 }
1455 }
1456
1457 PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName);
1458 if (pPattern)
1459 {
1460 RTListNodeRemove(&pPattern->ListNode);
1461 RTMemFree(pPattern->pvPattern);
1462 RTStrFree(pPattern->pszName);
1463 RTMemFree(pPattern);
1464 }
1465 else
1466 rc = VERR_NOT_FOUND;
1467
1468 return rc;
1469}
1470
1471static DECLCALLBACK(int) vdScriptHandlerSleep(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1472{
1473 int rc = VINF_SUCCESS;
1474 uint64_t cMillies = 0;
1475
1476 for (unsigned i = 0; i < cScriptArgs; i++)
1477 {
1478 switch (paScriptArgs[i].chId)
1479 {
1480 case 't':
1481 {
1482 cMillies = paScriptArgs[i].u.u64;
1483 break;
1484 }
1485 default:
1486 AssertMsgFailed(("Invalid argument given!\n"));
1487 }
1488 }
1489
1490 rc = RTThreadSleep(cMillies);
1491 return rc;
1492}
1493
1494static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1495{
1496 int rc = VINF_SUCCESS;
1497 const char *pcszFile = NULL;
1498 const char *pcszPathToDump = NULL;
1499
1500 for (unsigned i = 0; i < cScriptArgs; i++)
1501 {
1502 switch (paScriptArgs[i].chId)
1503 {
1504 case 'f':
1505 {
1506 pcszFile = paScriptArgs[i].u.pcszString;
1507 break;
1508 }
1509 case 'p':
1510 {
1511 pcszPathToDump = paScriptArgs[i].u.pcszString;
1512 break;
1513 }
1514 default:
1515 AssertMsgFailed(("Invalid argument given!\n"));
1516 }
1517 }
1518
1519 /* Check for the file. */
1520 PVDFILE pIt = NULL;
1521 bool fFound = false;
1522 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1523 {
1524 if (!RTStrCmp(pIt->pszName, pcszFile))
1525 {
1526 fFound = true;
1527 break;
1528 }
1529 }
1530
1531 if (fFound)
1532 {
1533 RTPrintf("Dumping memory file %s to %s, this might take some time\n", pcszFile, pcszPathToDump);
1534 rc = VDMemDiskWriteToFile(pIt->pMemDisk, pcszPathToDump);
1535 }
1536 else
1537 rc = VERR_FILE_NOT_FOUND;
1538
1539 return rc;
1540}
1541
1542static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1543{
1544 int rc = VINF_SUCCESS;
1545 const char *pcszDisk = NULL;
1546 PVDDISK pDisk = NULL;
1547 bool fVerify = false;
1548
1549 for (unsigned i = 0; i < cScriptArgs; i++)
1550 {
1551 switch (paScriptArgs[i].chId)
1552 {
1553 case 'n':
1554 {
1555 pcszDisk = paScriptArgs[i].u.pcszString;
1556 break;
1557 }
1558 case 'v':
1559 {
1560 fVerify = paScriptArgs[i].u.fFlag;
1561 break;
1562 }
1563 default:
1564 AssertMsgFailed(("Invalid argument given!\n"));
1565 }
1566 }
1567
1568 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1569 if (pDisk)
1570 rc = VERR_ALREADY_EXISTS;
1571 else
1572 {
1573 pDisk = (PVDDISK)RTMemAllocZ(sizeof(VDDISK));
1574 if (pDisk)
1575 {
1576 pDisk->pszName = RTStrDup(pcszDisk);
1577 if (pDisk->pszName)
1578 {
1579 rc = VINF_SUCCESS;
1580
1581 if (fVerify)
1582 {
1583 rc = VDMemDiskCreate(&pDisk->pMemDiskVerify, 0 /* Growing */);
1584 if (RT_SUCCESS(rc))
1585 {
1586 rc = RTCritSectInit(&pDisk->CritSectVerify);
1587 if (RT_FAILURE(rc))
1588 VDMemDiskDestroy(pDisk->pMemDiskVerify);
1589 }
1590 }
1591
1592 if (RT_SUCCESS(rc))
1593 {
1594 rc = VDCreate(pGlob->pInterfacesDisk, VDTYPE_HDD, &pDisk->pVD);
1595
1596 if (RT_SUCCESS(rc))
1597 RTListAppend(&pGlob->ListDisks, &pDisk->ListNode);
1598 else
1599 {
1600 if (fVerify)
1601 {
1602 RTCritSectDelete(&pDisk->CritSectVerify);
1603 VDMemDiskDestroy(pDisk->pMemDiskVerify);
1604 }
1605 RTStrFree(pDisk->pszName);
1606 }
1607 }
1608 }
1609 else
1610 rc = VERR_NO_MEMORY;
1611
1612 if (RT_FAILURE(rc))
1613 RTMemFree(pDisk);
1614 }
1615 else
1616 rc = VERR_NO_MEMORY;
1617 }
1618 return rc;
1619}
1620
1621static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1622{
1623 int rc = VINF_SUCCESS;
1624 const char *pcszDisk = NULL;
1625 PVDDISK pDisk = NULL;
1626
1627 for (unsigned i = 0; i < cScriptArgs; i++)
1628 {
1629 switch (paScriptArgs[i].chId)
1630 {
1631 case 'n':
1632 {
1633 pcszDisk = paScriptArgs[i].u.pcszString;
1634 break;
1635 }
1636 default:
1637 AssertMsgFailed(("Invalid argument given!\n"));
1638 }
1639 }
1640
1641 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1642 if (pDisk)
1643 {
1644 RTListNodeRemove(&pDisk->ListNode);
1645 VDDestroy(pDisk->pVD);
1646 if (pDisk->pMemDiskVerify)
1647 {
1648 VDMemDiskDestroy(pDisk->pMemDiskVerify);
1649 RTCritSectDelete(&pDisk->CritSectVerify);
1650 }
1651 RTStrFree(pDisk->pszName);
1652 RTMemFree(pDisk);
1653 }
1654 else
1655 rc = VERR_NOT_FOUND;
1656
1657 return rc;
1658}
1659
1660static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1661{
1662 int rc = VINF_SUCCESS;
1663 const char *pcszDisk1 = NULL;
1664 PVDDISK pDisk1 = NULL;
1665 const char *pcszDisk2 = NULL;
1666 PVDDISK pDisk2 = NULL;
1667
1668 for (unsigned i = 0; i < cScriptArgs; i++)
1669 {
1670 switch (paScriptArgs[i].chId)
1671 {
1672 case '1':
1673 {
1674 pcszDisk1 = paScriptArgs[i].u.pcszString;
1675 break;
1676 }
1677 case '2':
1678 {
1679 pcszDisk2 = paScriptArgs[i].u.pcszString;
1680 break;
1681 }
1682 default:
1683 AssertMsgFailed(("Invalid argument given!\n"));
1684 }
1685 }
1686
1687 pDisk1 = tstVDIoGetDiskByName(pGlob, pcszDisk1);
1688 pDisk2 = tstVDIoGetDiskByName(pGlob, pcszDisk2);
1689
1690 if (pDisk1 && pDisk2)
1691 {
1692 uint8_t *pbBuf1 = (uint8_t *)RTMemAllocZ(16 * _1M);
1693 uint8_t *pbBuf2 = (uint8_t *)RTMemAllocZ(16 * _1M);
1694 if (pbBuf1 && pbBuf2)
1695 {
1696 uint64_t cbDisk1, cbDisk2;
1697 uint64_t uOffCur = 0;
1698
1699 cbDisk1 = VDGetSize(pDisk1->pVD, VD_LAST_IMAGE);
1700 cbDisk2 = VDGetSize(pDisk2->pVD, VD_LAST_IMAGE);
1701
1702 if (cbDisk1 != cbDisk2)
1703 RTPrintf("Disks differ in size %llu vs %llu\n", cbDisk1, cbDisk2);
1704 else
1705 {
1706 while (uOffCur < cbDisk1)
1707 {
1708 size_t cbRead = RT_MIN(cbDisk1, 16 * _1M);
1709
1710 rc = VDRead(pDisk1->pVD, uOffCur, pbBuf1, cbRead);
1711 if (RT_SUCCESS(rc))
1712 rc = VDRead(pDisk2->pVD, uOffCur, pbBuf2, cbRead);
1713
1714 if (RT_SUCCESS(rc))
1715 {
1716 if (memcmp(pbBuf1, pbBuf2, cbRead))
1717 {
1718 RTPrintf("Disks differ at offset %llu\n", uOffCur);
1719 rc = VERR_DEV_IO_ERROR;
1720 break;
1721 }
1722 }
1723 else
1724 {
1725 RTPrintf("Reading one disk at offset %llu failed\n", uOffCur);
1726 break;
1727 }
1728
1729 uOffCur += cbRead;
1730 cbDisk1 -= cbRead;
1731 }
1732 }
1733 RTMemFree(pbBuf1);
1734 RTMemFree(pbBuf2);
1735 }
1736 else
1737 {
1738 if (pbBuf1)
1739 RTMemFree(pbBuf1);
1740 if (pbBuf2)
1741 RTMemFree(pbBuf2);
1742 rc = VERR_NO_MEMORY;
1743 }
1744 }
1745 else
1746 rc = VERR_NOT_FOUND;
1747
1748 return rc;
1749}
1750
1751static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1752{
1753 int rc = VINF_SUCCESS;
1754 const char *pcszDisk = NULL;
1755 PVDDISK pDisk = NULL;
1756
1757 for (unsigned i = 0; i < cScriptArgs; i++)
1758 {
1759 switch (paScriptArgs[i].chId)
1760 {
1761 case 'd':
1762 {
1763 pcszDisk = paScriptArgs[i].u.pcszString;
1764 break;
1765 }
1766 default:
1767 AssertMsgFailed(("Invalid argument given!\n"));
1768 }
1769 }
1770
1771 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1772
1773 if (pDisk)
1774 VDDumpImages(pDisk->pVD);
1775 else
1776 rc = VERR_NOT_FOUND;
1777
1778 return rc;
1779}
1780
1781static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1782{
1783 RTPrintf("%s\n", paScriptArgs[0].u.pcszString);
1784 return VINF_SUCCESS;
1785}
1786
1787static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation,
1788 uint32_t fOpen,
1789 PFNVDCOMPLETED pfnCompleted,
1790 void **ppStorage)
1791{
1792 int rc = VINF_SUCCESS;
1793 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1794 bool fFound = false;
1795
1796 /*
1797 * Some backends use ./ for paths, strip it.
1798 * @todo: Implement proper directory support for the
1799 * memory filesystem.
1800 */
1801 if ( strlen(pszLocation) >= 2
1802 && *pszLocation == '.'
1803 && pszLocation[1] == '/')
1804 pszLocation += 2;
1805
1806 /* Check if the file exists. */
1807 PVDFILE pIt = NULL;
1808 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1809 {
1810 if (!RTStrCmp(pIt->pszName, pszLocation))
1811 {
1812 fFound = true;
1813 break;
1814 }
1815 }
1816
1817 if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE)
1818 {
1819 /* If the file exists delete the memory disk. */
1820 if (fFound)
1821 rc = VDMemDiskSetSize(pIt->pMemDisk, 0);
1822 else
1823 {
1824 /* Create completey new. */
1825 pIt = (PVDFILE)RTMemAllocZ(sizeof(VDFILE));
1826 if (pIt)
1827 {
1828 pIt->pszName = RTStrDup(pszLocation);
1829
1830 if (pIt->pszName)
1831 {
1832 rc = VDMemDiskCreate(&pIt->pMemDisk, 0);
1833 }
1834 else
1835 rc = VERR_NO_MEMORY;
1836
1837 if (RT_FAILURE(rc))
1838 {
1839 if (pIt->pszName)
1840 RTStrFree(pIt->pszName);
1841 RTMemFree(pIt);
1842 }
1843 }
1844 else
1845 rc = VERR_NO_MEMORY;
1846
1847 RTListAppend(&pGlob->ListFiles, &pIt->Node);
1848 }
1849 }
1850 else if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN)
1851 {
1852 if (!fFound)
1853 rc = VERR_FILE_NOT_FOUND;
1854 }
1855 else
1856 rc = VERR_INVALID_PARAMETER;
1857
1858 if (RT_SUCCESS(rc))
1859 {
1860 AssertPtr(pIt);
1861 PVDSTORAGE pStorage = (PVDSTORAGE)RTMemAllocZ(sizeof(VDSTORAGE));
1862 if (!pStorage)
1863 rc = VERR_NO_MEMORY;
1864
1865 pStorage->pFile = pIt;
1866 pStorage->pfnComplete = pfnCompleted;
1867 *ppStorage = pStorage;
1868 }
1869
1870 return rc;
1871}
1872
1873static DECLCALLBACK(int) tstVDIoFileClose(void *pvUser, void *pStorage)
1874{
1875 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1876
1877 RTMemFree(pIoStorage);
1878 return VINF_SUCCESS;
1879}
1880
1881static DECLCALLBACK(int) tstVDIoFileDelete(void *pvUser, const char *pcszFilename)
1882{
1883 int rc = VINF_SUCCESS;
1884 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1885 bool fFound = false;
1886
1887 /*
1888 * Some backends use ./ for paths, strip it.
1889 * @todo: Implement proper directory support for the
1890 * memory filesystem.
1891 */
1892 if ( strlen(pcszFilename) >= 2
1893 && *pcszFilename == '.'
1894 && pcszFilename[1] == '/')
1895 pcszFilename += 2;
1896
1897 /* Check if the file exists. */
1898 PVDFILE pIt = NULL;
1899 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1900 {
1901 if (!RTStrCmp(pIt->pszName, pcszFilename))
1902 {
1903 fFound = true;
1904 break;
1905 }
1906 }
1907
1908 if (fFound)
1909 {
1910 RTListNodeRemove(&pIt->Node);
1911 VDMemDiskDestroy(pIt->pMemDisk);
1912 RTStrFree(pIt->pszName);
1913 RTMemFree(pIt);
1914 }
1915 else
1916 rc = VERR_FILE_NOT_FOUND;
1917
1918 return rc;
1919}
1920
1921static DECLCALLBACK(int) tstVDIoFileMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
1922{
1923 int rc = VINF_SUCCESS;
1924 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1925 bool fFound = false;
1926
1927 /* Check if the file exists. */
1928 PVDFILE pIt = NULL;
1929 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1930 {
1931 if (!RTStrCmp(pIt->pszName, pcszSrc))
1932 {
1933 fFound = true;
1934 break;
1935 }
1936 }
1937
1938 if (fFound)
1939 {
1940 char *pszNew = RTStrDup(pcszDst);
1941 if (pszNew)
1942 {
1943 RTStrFree(pIt->pszName);
1944 pIt->pszName = pszNew;
1945 }
1946 else
1947 rc = VERR_NO_MEMORY;
1948 }
1949 else
1950 rc = VERR_FILE_NOT_FOUND;
1951
1952 return rc;
1953}
1954
1955static DECLCALLBACK(int) tstVDIoFileGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
1956{
1957 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
1958
1959 *pcbFreeSpace = ~0ULL; /** @todo: Implement */
1960 return VINF_SUCCESS;
1961}
1962
1963static DECLCALLBACK(int) tstVDIoFileGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
1964{
1965 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
1966
1967 /** @todo: Implement */
1968 return VINF_SUCCESS;
1969}
1970
1971static DECLCALLBACK(int) tstVDIoFileGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
1972{
1973 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1974
1975 return VDMemDiskGetSize(pIoStorage->pFile->pMemDisk, pcbSize);
1976}
1977
1978static DECLCALLBACK(int) tstVDIoFileSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
1979{
1980 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1981
1982 return VDMemDiskSetSize(pIoStorage->pFile->pMemDisk, cbSize);
1983}
1984
1985static DECLCALLBACK(int) tstVDIoFileWriteSync(void *pvUser, void *pStorage, uint64_t uOffset,
1986 const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten)
1987{
1988 int rc = VINF_SUCCESS;
1989 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1990
1991 RTSGBUF SgBuf;
1992 RTSGSEG Seg;
1993
1994 Seg.pvSeg = (void *)pvBuffer;
1995 Seg.cbSeg = cbBuffer;
1996 RTSgBufInit(&SgBuf, &Seg, 1);
1997 rc = VDMemDiskWrite(pIoStorage->pFile->pMemDisk, uOffset, cbBuffer, &SgBuf);
1998 if (RT_SUCCESS(rc) && pcbWritten)
1999 *pcbWritten = cbBuffer;
2000
2001 return rc;
2002}
2003
2004static DECLCALLBACK(int) tstVDIoFileReadSync(void *pvUser, void *pStorage, uint64_t uOffset,
2005 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
2006{
2007 int rc = VINF_SUCCESS;
2008 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2009
2010 RTSGBUF SgBuf;
2011 RTSGSEG Seg;
2012
2013 Seg.pvSeg = pvBuffer;
2014 Seg.cbSeg = cbBuffer;
2015 RTSgBufInit(&SgBuf, &Seg, 1);
2016 rc = VDMemDiskRead(pIoStorage->pFile->pMemDisk, uOffset, cbBuffer, &SgBuf);
2017 if (RT_SUCCESS(rc) && pcbRead)
2018 *pcbRead = cbBuffer;
2019
2020 return rc;
2021}
2022
2023static DECLCALLBACK(int) tstVDIoFileFlushSync(void *pvUser, void *pStorage)
2024{
2025 /* nothing to do. */
2026 return VINF_SUCCESS;
2027}
2028
2029static DECLCALLBACK(int) tstVDIoFileReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
2030 PCRTSGSEG paSegments, size_t cSegments,
2031 size_t cbRead, void *pvCompletion,
2032 void **ppTask)
2033{
2034 int rc = VINF_SUCCESS;
2035 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2036 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2037
2038 rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_READ, uOffset,
2039 cbRead, paSegments, cSegments, pIoStorage->pfnComplete, pvCompletion);
2040 if (RT_SUCCESS(rc))
2041 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2042
2043 return rc;
2044}
2045
2046static DECLCALLBACK(int) tstVDIoFileWriteAsync(void *pvUser, void *pStorage, uint64_t uOffset,
2047 PCRTSGSEG paSegments, size_t cSegments,
2048 size_t cbWrite, void *pvCompletion,
2049 void **ppTask)
2050{
2051 int rc = VINF_SUCCESS;
2052 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2053 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2054
2055 rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_WRITE, uOffset,
2056 cbWrite, paSegments, cSegments, pIoStorage->pfnComplete, pvCompletion);
2057 if (RT_SUCCESS(rc))
2058 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2059
2060 return rc;
2061}
2062
2063static DECLCALLBACK(int) tstVDIoFileFlushAsync(void *pvUser, void *pStorage, void *pvCompletion,
2064 void **ppTask)
2065{
2066 int rc = VINF_SUCCESS;
2067 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2068 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2069
2070 rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_FLUSH, 0,
2071 0, NULL, 0, pIoStorage->pfnComplete, pvCompletion);
2072 if (RT_SUCCESS(rc))
2073 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2074
2075 return rc;
2076}
2077
2078static int tstVDIoTestInit(PVDIOTEST pIoTest, PVDTESTGLOB pGlob, bool fRandomAcc, uint64_t cbIo,
2079 size_t cbBlkSize, uint64_t offStart, uint64_t offEnd,
2080 unsigned uWriteChance, PVDPATTERN pPattern)
2081{
2082 int rc = VINF_SUCCESS;
2083
2084 pIoTest->fRandomAccess = fRandomAcc;
2085 pIoTest->cbIo = cbIo;
2086 pIoTest->cbBlkIo = cbBlkSize;
2087 pIoTest->offStart = offStart;
2088 pIoTest->offEnd = offEnd;
2089 pIoTest->uWriteChance = uWriteChance;
2090 pIoTest->pIoRnd = pGlob->pIoRnd;
2091 pIoTest->pPattern = pPattern;
2092
2093 if (fRandomAcc)
2094 {
2095 uint64_t cbRange = pIoTest->offEnd < pIoTest->offStart
2096 ? pIoTest->offStart - pIoTest->offEnd
2097 : pIoTest->offEnd - pIoTest->offStart;
2098
2099 pIoTest->u.Rnd.cBlocks = cbRange / cbBlkSize + ((cbRange % cbBlkSize) ? 1 : 0);
2100 pIoTest->u.Rnd.cBlocksLeft = pIoTest->u.Rnd.cBlocks;
2101 pIoTest->u.Rnd.pbMapAccessed = (uint8_t *)RTMemAllocZ(pIoTest->u.Rnd.cBlocks / 8
2102 + ((pIoTest->u.Rnd.cBlocks % 8)
2103 ? 1
2104 : 0));
2105 if (!pIoTest->u.Rnd.pbMapAccessed)
2106 rc = VERR_NO_MEMORY;
2107 }
2108 else
2109 pIoTest->u.offNext = pIoTest->offEnd < pIoTest->offStart ? pIoTest->offStart - cbBlkSize : 0;
2110
2111 return rc;
2112}
2113
2114static void tstVDIoTestDestroy(PVDIOTEST pIoTest)
2115{
2116 if (pIoTest->fRandomAccess)
2117 RTMemFree(pIoTest->u.Rnd.pbMapAccessed);
2118}
2119
2120static bool tstVDIoTestRunning(PVDIOTEST pIoTest)
2121{
2122 return pIoTest->cbIo > 0;
2123}
2124
2125static bool tstVDIoTestReqOutstanding(PVDIOREQ pIoReq)
2126{
2127 return pIoReq->fOutstanding;
2128}
2129
2130/**
2131 * Returns true with the given chance in percent.
2132 *
2133 * @returns true or false
2134 * @param iPercentage The percentage of the chance to return true.
2135 */
2136static bool tstVDIoTestIsTrue(PVDIOTEST pIoTest, int iPercentage)
2137{
2138 int uRnd = VDIoRndGetU32Ex(pIoTest->pIoRnd, 0, 100);
2139
2140 return (uRnd < iPercentage); /* This should be enough for our purpose */
2141}
2142
2143static int tstVDIoTestReqInit(PVDIOTEST pIoTest, PVDIOREQ pIoReq, void *pvUser)
2144{
2145 int rc = VINF_SUCCESS;
2146
2147 if (pIoTest->cbIo)
2148 {
2149 /* Read or Write? */
2150 pIoReq->enmTxDir = tstVDIoTestIsTrue(pIoTest, pIoTest->uWriteChance) ? VDIOREQTXDIR_WRITE : VDIOREQTXDIR_READ;
2151 pIoReq->cbReq = RT_MIN(pIoTest->cbBlkIo, pIoTest->cbIo);
2152 pIoTest->cbIo -= pIoReq->cbReq;
2153 pIoReq->DataSeg.cbSeg = pIoReq->cbReq;
2154
2155 if (pIoReq->enmTxDir == VDIOREQTXDIR_WRITE)
2156 {
2157 if (pIoTest->pPattern)
2158 rc = tstVDIoPatternGetBuffer(pIoTest->pPattern, &pIoReq->DataSeg.pvSeg, pIoReq->cbReq);
2159 else
2160 rc = VDIoRndGetBuffer(pIoTest->pIoRnd, &pIoReq->DataSeg.pvSeg, pIoReq->cbReq);
2161 AssertRC(rc);
2162 }
2163 else
2164 {
2165 /* Read */
2166 pIoReq->DataSeg.pvSeg = pIoReq->pvBufRead;
2167 }
2168
2169 if (RT_SUCCESS(rc))
2170 {
2171 RTSgBufInit(&pIoReq->SgBuf, &pIoReq->DataSeg, 1);
2172
2173 if (pIoTest->fRandomAccess)
2174 {
2175 int idx = -1;
2176
2177 idx = ASMBitFirstClear(pIoTest->u.Rnd.pbMapAccessed, pIoTest->u.Rnd.cBlocks);
2178
2179 /* In case this is the last request we don't need to search further. */
2180 if (pIoTest->u.Rnd.cBlocksLeft > 1)
2181 {
2182 int idxIo;
2183 idxIo = VDIoRndGetU32Ex(pIoTest->pIoRnd, idx, pIoTest->u.Rnd.cBlocks - 1);
2184
2185 /*
2186 * If the bit is marked free use it, otherwise search for the next free bit
2187 * and if that doesn't work use the first free bit.
2188 */
2189 if (ASMBitTest(pIoTest->u.Rnd.pbMapAccessed, idxIo))
2190 {
2191 idxIo = ASMBitNextClear(pIoTest->u.Rnd.pbMapAccessed, pIoTest->u.Rnd.cBlocks, idxIo);
2192 if (idxIo != -1)
2193 idx = idxIo;
2194 }
2195 else
2196 idx = idxIo;
2197 }
2198
2199 Assert(idx != -1);
2200 pIoReq->off = idx * pIoTest->cbBlkIo;
2201 pIoTest->u.Rnd.cBlocksLeft--;
2202 if (!pIoTest->u.Rnd.cBlocksLeft)
2203 {
2204 /* New round, clear everything. */
2205 ASMBitClearRange(pIoTest->u.Rnd.pbMapAccessed, 0, pIoTest->u.Rnd.cBlocks);
2206 pIoTest->u.Rnd.cBlocksLeft = pIoTest->u.Rnd.cBlocks;
2207 }
2208 else
2209 ASMBitSet(pIoTest->u.Rnd.pbMapAccessed, idx);
2210 }
2211 else
2212 {
2213 pIoReq->off = pIoTest->u.offNext;
2214 if (pIoTest->offEnd < pIoTest->offStart)
2215 {
2216 pIoTest->u.offNext = pIoTest->u.offNext == 0
2217 ? pIoTest->offEnd - pIoTest->cbBlkIo
2218 : RT_MAX(pIoTest->offEnd, pIoTest->u.offNext - pIoTest->cbBlkIo);
2219 }
2220 else
2221 {
2222 pIoTest->u.offNext = pIoTest->u.offNext + pIoTest->cbBlkIo >= pIoTest->offEnd
2223 ? 0
2224 : RT_MIN(pIoTest->offEnd, pIoTest->u.offNext + pIoTest->cbBlkIo);
2225 }
2226 }
2227 pIoReq->pvUser = pvUser;
2228 pIoReq->fOutstanding = true;
2229 }
2230 }
2231 else
2232 rc = VERR_ACCESS_DENIED;
2233
2234 return rc;
2235}
2236
2237static void tstVDIoTestReqComplete(void *pvUser1, void *pvUser2, int rcReq)
2238{
2239 PVDIOREQ pIoReq = (PVDIOREQ)pvUser1;
2240 RTSEMEVENT hEventSem = (RTSEMEVENT)pvUser2;
2241 PVDDISK pDisk = (PVDDISK)pIoReq->pvUser;
2242
2243 LogFlow(("Request %d completed\n", pIoReq->idx));
2244
2245 if (pDisk->pMemDiskVerify)
2246 {
2247 switch (pIoReq->enmTxDir)
2248 {
2249 case VDIOREQTXDIR_READ:
2250 {
2251 RTCritSectEnter(&pDisk->CritSectVerify);
2252 RTSgBufReset(&pIoReq->SgBuf);
2253
2254 if (VDMemDiskCmp(pDisk->pMemDiskVerify, pIoReq->off, pIoReq->cbReq,
2255 &pIoReq->SgBuf))
2256 RTPrintf("Corrupted disk at offset %llu!\n", pIoReq->off);
2257 RTCritSectLeave(&pDisk->CritSectVerify);
2258 }
2259 case VDIOREQTXDIR_WRITE:
2260 {
2261 RTCritSectEnter(&pDisk->CritSectVerify);
2262 RTSgBufReset(&pIoReq->SgBuf);
2263
2264 int rc = VDMemDiskWrite(pDisk->pMemDiskVerify, pIoReq->off, pIoReq->cbReq,
2265 &pIoReq->SgBuf);
2266 AssertRC(rc);
2267 RTCritSectLeave(&pDisk->CritSectVerify);
2268 break;
2269 }
2270 case VDIOREQTXDIR_FLUSH:
2271 break;
2272 }
2273 }
2274
2275 ASMAtomicXchgBool(&pIoReq->fOutstanding, false);
2276 RTSemEventSignal(hEventSem);
2277 return;
2278}
2279
2280/**
2281 * Returns the disk handle by name or NULL if not found
2282 *
2283 * @returns Disk handle or NULL if the disk could not be found.
2284 *
2285 * @param pGlob Global test state.
2286 * @param pcszDisk Name of the disk to get.
2287 */
2288static PVDDISK tstVDIoGetDiskByName(PVDTESTGLOB pGlob, const char *pcszDisk)
2289{
2290 PVDDISK pIt = NULL;
2291 bool fFound = false;
2292
2293 LogFlowFunc(("pGlob=%#p pcszDisk=%s\n", pGlob, pcszDisk));
2294
2295 RTListForEach(&pGlob->ListDisks, pIt, VDDISK, ListNode)
2296 {
2297 if (!RTStrCmp(pIt->pszName, pcszDisk))
2298 {
2299 fFound = true;
2300 break;
2301 }
2302 }
2303
2304 LogFlowFunc(("return %#p\n", fFound ? pIt : NULL));
2305 return fFound ? pIt : NULL;
2306}
2307
2308/**
2309 * Returns the I/O pattern handle by name of NULL if not found.
2310 *
2311 * @returns I/O pattern handle or NULL if the pattern could not be found.
2312 *
2313 * @param pGlob Global test state.
2314 * @param pcszName Name of the pattern.
2315 */
2316static PVDPATTERN tstVDIoGetPatternByName(PVDTESTGLOB pGlob, const char *pcszName)
2317{
2318 PVDPATTERN pIt = NULL;
2319 bool fFound = false;
2320
2321 LogFlowFunc(("pGlob=%#p pcszName=%s\n", pGlob, pcszName));
2322
2323 RTListForEach(&pGlob->ListPatterns, pIt, VDPATTERN, ListNode)
2324 {
2325 if (!RTStrCmp(pIt->pszName, pcszName))
2326 {
2327 fFound = true;
2328 break;
2329 }
2330 }
2331
2332 LogFlowFunc(("return %#p\n", fFound ? pIt : NULL));
2333 return fFound ? pIt : NULL;
2334}
2335
2336/**
2337 * Creates a new pattern with the given name and an
2338 * allocated pattern buffer.
2339 *
2340 * @returns Pointer to a new pattern buffer or NULL on failure.
2341 * @param pcszName Name of the pattern.
2342 * @param cbPattern Size of the pattern buffer.
2343 */
2344static PVDPATTERN tstVDIoPatternCreate(const char *pcszName, size_t cbPattern)
2345{
2346 PVDPATTERN pPattern = (PVDPATTERN)RTMemAllocZ(sizeof(VDPATTERN));
2347 char *pszName = RTStrDup(pcszName);
2348 void *pvPattern = RTMemAllocZ(cbPattern);
2349
2350 if (pPattern && pszName && pvPattern)
2351 {
2352 pPattern->pszName = pszName;
2353 pPattern->pvPattern = pvPattern;
2354 pPattern->cbPattern = cbPattern;
2355 }
2356 else
2357 {
2358 if (pPattern)
2359 RTMemFree(pPattern);
2360 if (pszName)
2361 RTStrFree(pszName);
2362 if (pvPattern)
2363 RTMemFree(pvPattern);
2364
2365 pPattern = NULL;
2366 pszName = NULL;
2367 pvPattern = NULL;
2368 }
2369
2370 return pPattern;
2371}
2372
2373static int tstVDIoPatternGetBuffer(PVDPATTERN pPattern, void **ppv, size_t cb)
2374{
2375 AssertPtrReturn(pPattern, VERR_INVALID_POINTER);
2376 AssertPtrReturn(ppv, VERR_INVALID_POINTER);
2377 AssertReturn(cb > 0, VERR_INVALID_PARAMETER);
2378
2379 if (cb > pPattern->cbPattern)
2380 return VERR_INVALID_PARAMETER;
2381
2382 *ppv = pPattern->pvPattern;
2383 return VINF_SUCCESS;
2384}
2385
2386/**
2387 * Skips the characters until the given character is reached.
2388 *
2389 * @returns Start of the string with the given character
2390 * or NULL if the string ended before.
2391 *
2392 * @param psz The string to skip.
2393 * @param ch The character.
2394 */
2395static char *tstVDIoScriptSkipUntil(char *psz, char ch)
2396{
2397 while ( *psz != '\0'
2398 && *psz != ch)
2399 psz++;
2400
2401 return psz;
2402}
2403
2404/**
2405 * Skips the spaces of the current string.
2406 *
2407 * @returns Start of the string with a non space character
2408 * or NULL if the string ended before.
2409 *
2410 * @param psz The string to skip.
2411 */
2412static char *tstVDIoScriptSkipSpace(char *psz)
2413{
2414 while ( *psz != '\0'
2415 && RT_C_IS_SPACE(*psz))
2416 psz++;
2417
2418 return psz;
2419}
2420
2421/**
2422 * Skips all characters until a space is reached of the current
2423 * string.
2424 *
2425 * @returns Start of the string with a space character
2426 * or NULL if the string ended before.
2427 *
2428 * @param psz The string to skip.
2429 */
2430static char *tstVDIoScriptSkipNonSpace(char *psz)
2431{
2432 while ( *psz != '\0'
2433 && !RT_C_IS_SPACE(*psz))
2434 psz++;
2435
2436 return psz;
2437}
2438
2439/**
2440 * Returns true if the first character of the given string
2441 * contains a character marking a line end (comment or \0
2442 * terminator).
2443 *
2444 * @returns true if the line contains no more characters of
2445 * interest and false otherwise.
2446 *
2447 * @param psz The string to check for.
2448 */
2449static bool tstVDIoIsLineEnd(const char *psz)
2450{
2451 return *psz == '\0' || *psz == '#';
2452}
2453
2454/**
2455 * Parses one argument name, value pair.
2456 *
2457 * @returns IPRT status code.
2458 *
2459 * @param pVDScriptAction Script action.
2460 * @param pcszName Argument name.
2461 * @param pcszValue Argument value.
2462 * @param pScriptArg Where to fill in the parsed
2463 * argument.
2464 * @param pfMandatory Where to store whether the argument
2465 * is mandatory.
2466 */
2467static int tstVDIoScriptArgumentParse(PCVDSCRIPTACTION pVDScriptAction, const char *pcszName,
2468 const char *pcszValue, PVDSCRIPTARG pScriptArg, bool *pfMandatory)
2469{
2470 int rc = VERR_NOT_FOUND;
2471
2472 for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
2473 {
2474 if (!RTStrCmp(pVDScriptAction->paArgDesc[i].pcszName, pcszName))
2475 {
2476 rc = VINF_SUCCESS;
2477
2478 switch (pVDScriptAction->paArgDesc[i].enmType)
2479 {
2480 case VDSCRIPTARGTYPE_BOOL:
2481 {
2482 pScriptArg->enmType = VDSCRIPTARGTYPE_BOOL;
2483 if (!RTStrICmp(pcszValue, "yes") || !RTStrICmp(pcszValue, "on"))
2484 pScriptArg->u.fFlag = true;
2485 else if (!RTStrICmp(pcszValue, "no") || !RTStrICmp(pcszValue, "off"))
2486 pScriptArg->u.fFlag = false;
2487 else
2488 {
2489 RTPrintf("Boolean argument malformed '%s'\n", pcszValue);
2490 rc = VERR_INVALID_PARAMETER;
2491 }
2492 break;
2493 }
2494 case VDSCRIPTARGTYPE_SIGNED_NUMBER:
2495 {
2496 pScriptArg->enmType = VDSCRIPTARGTYPE_SIGNED_NUMBER;
2497 AssertMsgFailed(("todo\n"));
2498 break;
2499 }
2500 case VDSCRIPTARGTYPE_STRING:
2501 {
2502 pScriptArg->enmType = VDSCRIPTARGTYPE_STRING;
2503 pScriptArg->u.pcszString = pcszValue;
2504 break;
2505 }
2506 case VDSCRIPTARGTYPE_UNSIGNED_NUMBER:
2507 {
2508 char *pszSuffix = NULL;
2509
2510 pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_NUMBER;
2511 rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.u64);
2512 if (rc == VWRN_TRAILING_CHARS)
2513 {
2514 switch (*pszSuffix)
2515 {
2516 case 'k':
2517 case 'K':
2518 {
2519 pScriptArg->u.u64 *= _1K;
2520 break;
2521 }
2522 case 'm':
2523 case 'M':
2524 {
2525 pScriptArg->u.u64 *= _1M;
2526 break;
2527 }
2528 case 'g':
2529 case 'G':
2530 {
2531 pScriptArg->u.u64 *= _1G;
2532 break;
2533 }
2534 default:
2535 {
2536 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
2537 rc = VERR_INVALID_PARAMETER;
2538 }
2539 }
2540 if (rc != VERR_INVALID_PARAMETER)
2541 rc = VINF_SUCCESS;
2542 }
2543
2544 break;
2545 }
2546 case VDSCRIPTARGTYPE_UNSIGNED_RANGE:
2547 {
2548 char *pszSuffix = NULL;
2549
2550 pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_RANGE;
2551 rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.Range.Start);
2552 if (rc == VWRN_TRAILING_CHARS)
2553 {
2554 if (*pszSuffix != '-')
2555 {
2556 switch (*pszSuffix)
2557 {
2558 case 'k':
2559 case 'K':
2560 {
2561 pScriptArg->u.u64 *= _1K;
2562 break;
2563 }
2564 case 'm':
2565 case 'M':
2566 {
2567 pScriptArg->u.u64 *= _1M;
2568 break;
2569 }
2570 case 'g':
2571 case 'G':
2572 {
2573 pScriptArg->u.u64 *= _1G;
2574 break;
2575 }
2576 default:
2577 {
2578 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
2579 rc = VERR_INVALID_PARAMETER;
2580 }
2581 }
2582 if (RT_SUCCESS(rc))
2583 pszSuffix++;
2584 }
2585
2586 if (*pszSuffix == '-')
2587 {
2588 pszSuffix++;
2589 rc = RTStrToUInt64Ex(pszSuffix, &pszSuffix, 10, &pScriptArg->u.Range.End);
2590 if (rc == VWRN_TRAILING_CHARS)
2591 {
2592 switch (*pszSuffix)
2593 {
2594 case 'k':
2595 case 'K':
2596 {
2597 pScriptArg->u.Range.End *= _1K;
2598 break;
2599 }
2600 case 'm':
2601 case 'M':
2602 {
2603 pScriptArg->u.Range.End *= _1M;
2604 break;
2605 }
2606 case 'g':
2607 case 'G':
2608 {
2609 pScriptArg->u.Range.End *= _1G;
2610 break;
2611 }
2612 default:
2613 {
2614 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
2615 rc = VERR_INVALID_PARAMETER;
2616 }
2617 }
2618 }
2619 }
2620 else
2621 rc = VERR_INVALID_PARAMETER;
2622 }
2623 else
2624 rc = VERR_INVALID_PARAMETER;
2625
2626 if (rc == VERR_INVALID_PARAMETER)
2627 RTPrintf("Invalid range format\n");
2628 break;
2629 }
2630 default:
2631 AssertMsgFailed(("Invalid script argument type\n"));
2632 }
2633
2634 if (RT_SUCCESS(rc))
2635 {
2636 pScriptArg->chId = pVDScriptAction->paArgDesc[i].chId;
2637 *pfMandatory = !!(pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY);
2638 }
2639 break;
2640 }
2641 }
2642
2643 if (rc == VERR_NOT_FOUND)
2644 RTPrintf("Argument '%s' not found\n", pcszName);
2645
2646 return rc;
2647}
2648
2649/**
2650 * Parses the arguments of a action in the script.
2651 *
2652 * @returns IPRT status code.
2653 *
2654 * @param psz Argument string.
2655 * @param pVDScriptAction The script action to parses
2656 * arguments for.
2657 * @param paScriptArgs Where to store the arguments.
2658 * @param pcScriptArgs Where to store the actual number of
2659 * arguments parsed.
2660 */
2661static int tstVDIoScriptArgumentListParse(char *psz, PCVDSCRIPTACTION pVDScriptAction, PVDSCRIPTARG paScriptArgs, unsigned *pcScriptArgs)
2662{
2663 int rc = VINF_SUCCESS;
2664 unsigned cMandatoryArgsReq = 0;
2665 unsigned cScriptArgs = 0;
2666
2667 /* Count the number of mandatory arguments first. */
2668 for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
2669 if (pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY)
2670 cMandatoryArgsReq++;
2671
2672 /* One argument is given in the form name=value. */
2673 *pcScriptArgs = 0;
2674
2675 while ( psz
2676 && !tstVDIoIsLineEnd(psz))
2677 {
2678 const char *pcszName = psz;
2679
2680 psz = tstVDIoScriptSkipUntil(psz, '=');
2681 if (!tstVDIoIsLineEnd(psz))
2682 {
2683 *psz = '\0'; /* Overwrite */
2684 psz++;
2685 const char *pcszValue = psz;
2686
2687 psz = tstVDIoScriptSkipNonSpace(psz);
2688 if (!tstVDIoIsLineEnd(psz))
2689 {
2690 *psz = '\0'; /* Overwrite */
2691 psz++;
2692 psz = tstVDIoScriptSkipSpace(psz);
2693 }
2694
2695 pcszValue = tstVDIoScriptSkipSpace((char *)pcszValue);
2696 if (*pcszValue == '\0')
2697 {
2698 RTPrintf("Value missing for argument '%s'\n", pcszName);
2699 rc = VERR_INVALID_STATE;
2700 break;
2701 }
2702
2703 /* We have the name and value pair now. */
2704 bool fMandatory = false; /* Shut up gcc */
2705 rc = tstVDIoScriptArgumentParse(pVDScriptAction, pcszName, pcszValue, &paScriptArgs[cScriptArgs], &fMandatory);
2706 if (RT_SUCCESS(rc))
2707 {
2708 if (fMandatory)
2709 cMandatoryArgsReq--;
2710 cScriptArgs++;
2711 }
2712 }
2713 else
2714 {
2715 RTPrintf("Argument in invalid form\n");
2716 rc = VERR_INVALID_STATE;
2717 break;
2718 }
2719 }
2720
2721 if ( RT_SUCCESS(rc)
2722 && cMandatoryArgsReq)
2723 {
2724 /* No arguments anymore but there are still mandatory arguments left. */
2725 RTPrintf("There are %u arguments missing for script action '%s\n", pVDScriptAction->pcszAction);
2726 rc = VERR_INVALID_STATE;
2727 }
2728
2729 if (RT_SUCCESS(rc))
2730 *pcScriptArgs = cScriptArgs;
2731
2732 return rc;
2733}
2734
2735/**
2736 * Executes the script pointed to by the given stream.
2737 *
2738 * @returns IPRT status code.
2739 *
2740 * @param pStrm The stream handle of the script.
2741 * @param pGlob Global test data.
2742 */
2743static int tstVDIoScriptExecute(PRTSTREAM pStrm, PVDTESTGLOB pGlob)
2744{
2745 int rc = VINF_SUCCESS;
2746 char abBuffer[0x1000]; /* Current assumption that a line is never longer than 4096 bytes. */
2747 PVDSCRIPTARG paScriptArgs = NULL;
2748 unsigned cScriptArgsMax = 0;
2749
2750 do
2751 {
2752 memset(abBuffer, 0, sizeof(abBuffer));
2753 rc = RTStrmGetLine(pStrm, abBuffer, sizeof(abBuffer));
2754 if (RT_SUCCESS(rc))
2755 {
2756 const char *pcszAction = NULL;
2757 char *psz = abBuffer;
2758
2759 /* Skip space */
2760 psz = tstVDIoScriptSkipSpace(psz);
2761 if (!tstVDIoIsLineEnd(psz))
2762 {
2763 PCVDSCRIPTACTION pVDScriptAction = NULL;
2764
2765 /* Get the action name. */
2766 pcszAction = psz;
2767
2768 psz = tstVDIoScriptSkipNonSpace(psz);
2769 if (!tstVDIoIsLineEnd(psz))
2770 {
2771 Assert(RT_C_IS_SPACE(*psz));
2772 *psz++ = '\0';
2773 }
2774
2775 /* Find the action. */
2776 for (unsigned i = 0; i < g_cScriptActions; i++)
2777 {
2778 if (!RTStrCmp(pcszAction, g_aScriptActions[i].pcszAction))
2779 {
2780 pVDScriptAction = &g_aScriptActions[i];
2781 break;
2782 }
2783 }
2784
2785 if (pVDScriptAction)
2786 {
2787 /* Parse arguments. */
2788 if (cScriptArgsMax < pVDScriptAction->cArgDescs)
2789 {
2790 /* Increase arguments array. */
2791 if (paScriptArgs)
2792 RTMemFree(paScriptArgs);
2793
2794 cScriptArgsMax = pVDScriptAction->cArgDescs;
2795 paScriptArgs = (PVDSCRIPTARG)RTMemAllocZ(cScriptArgsMax * sizeof(VDSCRIPTARG));
2796 }
2797
2798 if (paScriptArgs)
2799 {
2800 unsigned cScriptArgs;
2801
2802 rc = tstVDIoScriptArgumentListParse(psz, pVDScriptAction, paScriptArgs, &cScriptArgs);
2803 if (RT_SUCCESS(rc))
2804 {
2805 /* Execute the handler. */
2806 rc = pVDScriptAction->pfnHandler(pGlob, paScriptArgs, cScriptArgs);
2807 }
2808 }
2809 else
2810 {
2811 RTPrintf("Out of memory while allocating argument array for script action %s\n", pcszAction);
2812 rc = VERR_NO_MEMORY;
2813 }
2814 }
2815 else
2816 {
2817 RTPrintf("Script action %s is not known\n", pcszAction);
2818 rc = VERR_NOT_FOUND;
2819 }
2820 }
2821 /* else empty line, just continue */
2822 }
2823 } while(RT_SUCCESS(rc));
2824
2825 if (rc == VERR_EOF)
2826 {
2827 RTPrintf("Successfully executed I/O script\n");
2828 rc = VINF_SUCCESS;
2829 }
2830 return rc;
2831}
2832
2833/**
2834 * Executes the given I/O script.
2835 *
2836 * @returns nothing.
2837 *
2838 * @param pcszFilename The script to execute.
2839 */
2840static void tstVDIoScriptRun(const char *pcszFilename)
2841{
2842 int rc = VINF_SUCCESS;
2843 PRTSTREAM pScriptStrm; /**< Stream of the script file. */
2844 VDTESTGLOB GlobTest; /**< Global test data. */
2845
2846 memset(&GlobTest, 0, sizeof(VDTESTGLOB));
2847 RTListInit(&GlobTest.ListFiles);
2848 RTListInit(&GlobTest.ListDisks);
2849 RTListInit(&GlobTest.ListPatterns);
2850
2851 rc = RTStrmOpen(pcszFilename, "r", &pScriptStrm);
2852 if (RT_SUCCESS(rc))
2853 {
2854 /* Init global test data. */
2855 GlobTest.VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
2856 GlobTest.VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
2857 GlobTest.VDIErrorCallbacks.pfnError = tstVDError;
2858 GlobTest.VDIErrorCallbacks.pfnMessage = tstVDMessage;
2859
2860 rc = VDInterfaceAdd(&GlobTest.VDIError, "tstVDIo_VDIError", VDINTERFACETYPE_ERROR,
2861 &GlobTest.VDIErrorCallbacks, NULL, &GlobTest.pInterfacesDisk);
2862 AssertRC(rc);
2863
2864 GlobTest.VDIIoCallbacks.cbSize = sizeof(VDINTERFACEIO);
2865 GlobTest.VDIIoCallbacks.enmInterface = VDINTERFACETYPE_IO;
2866 GlobTest.VDIIoCallbacks.pfnOpen = tstVDIoFileOpen;
2867 GlobTest.VDIIoCallbacks.pfnClose = tstVDIoFileClose;
2868 GlobTest.VDIIoCallbacks.pfnDelete = tstVDIoFileDelete;
2869 GlobTest.VDIIoCallbacks.pfnMove = tstVDIoFileMove;
2870 GlobTest.VDIIoCallbacks.pfnGetFreeSpace = tstVDIoFileGetFreeSpace;
2871 GlobTest.VDIIoCallbacks.pfnGetModificationTime = tstVDIoFileGetModificationTime;
2872 GlobTest.VDIIoCallbacks.pfnGetSize = tstVDIoFileGetSize;
2873 GlobTest.VDIIoCallbacks.pfnSetSize = tstVDIoFileSetSize;
2874 GlobTest.VDIIoCallbacks.pfnWriteSync = tstVDIoFileWriteSync;
2875 GlobTest.VDIIoCallbacks.pfnReadSync = tstVDIoFileReadSync;
2876 GlobTest.VDIIoCallbacks.pfnFlushSync = tstVDIoFileFlushSync;
2877 GlobTest.VDIIoCallbacks.pfnReadAsync = tstVDIoFileReadAsync;
2878 GlobTest.VDIIoCallbacks.pfnWriteAsync = tstVDIoFileWriteAsync;
2879 GlobTest.VDIIoCallbacks.pfnFlushAsync = tstVDIoFileFlushAsync;
2880
2881 rc = VDInterfaceAdd(&GlobTest.VDIIo, "tstVDIo_VDIIo", VDINTERFACETYPE_IO,
2882 &GlobTest.VDIIoCallbacks, &GlobTest, &GlobTest.pInterfacesImages);
2883 AssertRC(rc);
2884
2885 /* Init I/O backend. */
2886 rc = VDIoBackendMemCreate(&GlobTest.pIoBackend);
2887 if (RT_SUCCESS(rc))
2888 {
2889 /* Execute the script. */
2890 rc = tstVDIoScriptExecute(pScriptStrm, &GlobTest);
2891 if (RT_FAILURE(rc))
2892 {
2893 RTPrintf("Executing the script stream failed rc=%Rrc\n", rc);
2894 }
2895 VDIoBackendMemDestroy(GlobTest.pIoBackend);
2896 }
2897 else
2898 RTPrintf("Creating the I/O backend failed rc=%Rrc\n");
2899
2900 RTStrmClose(pScriptStrm);
2901 }
2902 else
2903 RTPrintf("Opening script failed rc=%Rrc\n", rc);
2904}
2905
2906/**
2907 * Shows help message.
2908 */
2909static void printUsage(void)
2910{
2911 RTPrintf("Usage:\n"
2912 "--script <filename> Script to execute\n"
2913 "--replay <filename> Log to replay (not implemented yet)\n");
2914}
2915
2916static const RTGETOPTDEF g_aOptions[] =
2917{
2918 { "--script", 's', RTGETOPT_REQ_STRING },
2919 { "--replay", 'r', RTGETOPT_REQ_STRING },
2920};
2921
2922int main(int argc, char *argv[])
2923{
2924 RTR3Init();
2925 int rc;
2926 RTGETOPTUNION ValueUnion;
2927 RTGETOPTSTATE GetState;
2928 char c;
2929
2930 if (argc != 3)
2931 {
2932 printUsage();
2933 return RTEXITCODE_FAILURE;
2934 }
2935
2936 rc = VDInit();
2937 if (RT_FAILURE(rc))
2938 return RTEXITCODE_FAILURE;
2939
2940 RTGetOptInit(&GetState, argc, argv, g_aOptions,
2941 RT_ELEMENTS(g_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
2942
2943 while ( RT_SUCCESS(rc)
2944 && (c = RTGetOpt(&GetState, &ValueUnion)))
2945 {
2946 switch (c)
2947 {
2948 case 's':
2949 tstVDIoScriptRun(ValueUnion.psz);
2950 break;
2951 case 'r':
2952 RTPrintf("Replaying I/O logs is not implemented yet\n");
2953 break;
2954 default:
2955 printUsage();
2956 }
2957 }
2958
2959 rc = VDShutdown();
2960 if (RT_FAILURE(rc))
2961 RTPrintf("tstVDIo: unloading backends failed! rc=%Rrc\n", rc);
2962
2963 return RTEXITCODE_SUCCESS;
2964}
2965
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