VirtualBox

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

最後變更 在這個檔案從38636是 38636,由 vboxsync 提交於 14 年 前

*,IPRT: Redid the ring-3 init to always convert the arguments to UTF-8.

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

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