VirtualBox

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

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

Storage/tstVDIo: Bugfixes

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