VirtualBox

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

Last change on this file since 38464 was 38462, checked in by vboxsync, 13 years ago

tstVDIo: Updates

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