VirtualBox

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

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

Storage/testcase: gcc-4.7 warning (probably false positive)

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