VirtualBox

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

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

VDDbg+DrvDiskIntegrity+tstVDIo: Bugfixes and add a I/O log replay action for the scripted I/O testcase

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