VirtualBox

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

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

Storage: Add async discard API

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 115.9 KB
Line 
1/* $Id: tstVDIo.cpp 38876 2011-09-27 09:03:15Z 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 pIoTest->fRandomAccess = fRandomAcc;
2712 pIoTest->cbIo = cbIo;
2713 pIoTest->cbBlkIo = cbBlkSize;
2714 pIoTest->offStart = offStart;
2715 pIoTest->offEnd = offEnd;
2716 pIoTest->uWriteChance = uWriteChance;
2717 pIoTest->pIoRnd = pGlob->pIoRnd;
2718 pIoTest->pPattern = pPattern;
2719
2720 if (fRandomAcc)
2721 {
2722 uint64_t cbRange = pIoTest->offEnd < pIoTest->offStart
2723 ? pIoTest->offStart - pIoTest->offEnd
2724 : pIoTest->offEnd - pIoTest->offStart;
2725
2726 pIoTest->u.Rnd.cBlocks = cbRange / cbBlkSize + ((cbRange % cbBlkSize) ? 1 : 0);
2727 pIoTest->u.Rnd.cBlocksLeft = pIoTest->u.Rnd.cBlocks;
2728 pIoTest->u.Rnd.pbMapAccessed = (uint8_t *)RTMemAllocZ(pIoTest->u.Rnd.cBlocks / 8
2729 + ((pIoTest->u.Rnd.cBlocks % 8)
2730 ? 1
2731 : 0));
2732 if (!pIoTest->u.Rnd.pbMapAccessed)
2733 rc = VERR_NO_MEMORY;
2734 }
2735 else
2736 pIoTest->u.offNext = pIoTest->offEnd < pIoTest->offStart ? pIoTest->offStart - cbBlkSize : 0;
2737
2738 return rc;
2739}
2740
2741static void tstVDIoTestDestroy(PVDIOTEST pIoTest)
2742{
2743 if (pIoTest->fRandomAccess)
2744 RTMemFree(pIoTest->u.Rnd.pbMapAccessed);
2745}
2746
2747static bool tstVDIoTestRunning(PVDIOTEST pIoTest)
2748{
2749 return pIoTest->cbIo > 0;
2750}
2751
2752static bool tstVDIoTestReqOutstanding(PVDIOREQ pIoReq)
2753{
2754 return pIoReq->fOutstanding;
2755}
2756
2757/**
2758 * Returns true with the given chance in percent.
2759 *
2760 * @returns true or false
2761 * @param iPercentage The percentage of the chance to return true.
2762 */
2763static bool tstVDIoTestIsTrue(PVDIOTEST pIoTest, int iPercentage)
2764{
2765 int uRnd = VDIoRndGetU32Ex(pIoTest->pIoRnd, 0, 100);
2766
2767 return (uRnd < iPercentage); /* This should be enough for our purpose */
2768}
2769
2770static int tstVDIoTestReqInit(PVDIOTEST pIoTest, PVDIOREQ pIoReq, void *pvUser)
2771{
2772 int rc = VINF_SUCCESS;
2773
2774 if (pIoTest->cbIo)
2775 {
2776 /* Read or Write? */
2777 pIoReq->enmTxDir = tstVDIoTestIsTrue(pIoTest, pIoTest->uWriteChance) ? VDIOREQTXDIR_WRITE : VDIOREQTXDIR_READ;
2778 pIoReq->cbReq = RT_MIN(pIoTest->cbBlkIo, pIoTest->cbIo);
2779 pIoTest->cbIo -= pIoReq->cbReq;
2780 pIoReq->DataSeg.cbSeg = pIoReq->cbReq;
2781
2782 if (pIoReq->enmTxDir == VDIOREQTXDIR_WRITE)
2783 {
2784 if (pIoTest->pPattern)
2785 rc = tstVDIoPatternGetBuffer(pIoTest->pPattern, &pIoReq->DataSeg.pvSeg, pIoReq->cbReq);
2786 else
2787 rc = VDIoRndGetBuffer(pIoTest->pIoRnd, &pIoReq->DataSeg.pvSeg, pIoReq->cbReq);
2788 AssertRC(rc);
2789 }
2790 else
2791 {
2792 /* Read */
2793 pIoReq->DataSeg.pvSeg = pIoReq->pvBufRead;
2794 }
2795
2796 if (RT_SUCCESS(rc))
2797 {
2798 RTSgBufInit(&pIoReq->SgBuf, &pIoReq->DataSeg, 1);
2799
2800 if (pIoTest->fRandomAccess)
2801 {
2802 int idx = -1;
2803
2804 idx = ASMBitFirstClear(pIoTest->u.Rnd.pbMapAccessed, pIoTest->u.Rnd.cBlocks);
2805
2806 /* In case this is the last request we don't need to search further. */
2807 if (pIoTest->u.Rnd.cBlocksLeft > 1)
2808 {
2809 int idxIo;
2810 idxIo = VDIoRndGetU32Ex(pIoTest->pIoRnd, idx, pIoTest->u.Rnd.cBlocks - 1);
2811
2812 /*
2813 * If the bit is marked free use it, otherwise search for the next free bit
2814 * and if that doesn't work use the first free bit.
2815 */
2816 if (ASMBitTest(pIoTest->u.Rnd.pbMapAccessed, idxIo))
2817 {
2818 idxIo = ASMBitNextClear(pIoTest->u.Rnd.pbMapAccessed, pIoTest->u.Rnd.cBlocks, idxIo);
2819 if (idxIo != -1)
2820 idx = idxIo;
2821 }
2822 else
2823 idx = idxIo;
2824 }
2825
2826 Assert(idx != -1);
2827 pIoReq->off = idx * pIoTest->cbBlkIo;
2828 pIoTest->u.Rnd.cBlocksLeft--;
2829 if (!pIoTest->u.Rnd.cBlocksLeft)
2830 {
2831 /* New round, clear everything. */
2832 ASMBitClearRange(pIoTest->u.Rnd.pbMapAccessed, 0, pIoTest->u.Rnd.cBlocks);
2833 pIoTest->u.Rnd.cBlocksLeft = pIoTest->u.Rnd.cBlocks;
2834 }
2835 else
2836 ASMBitSet(pIoTest->u.Rnd.pbMapAccessed, idx);
2837 }
2838 else
2839 {
2840 pIoReq->off = pIoTest->u.offNext;
2841 if (pIoTest->offEnd < pIoTest->offStart)
2842 {
2843 pIoTest->u.offNext = pIoTest->u.offNext == 0
2844 ? pIoTest->offEnd - pIoTest->cbBlkIo
2845 : RT_MAX(pIoTest->offEnd, pIoTest->u.offNext - pIoTest->cbBlkIo);
2846 }
2847 else
2848 {
2849 pIoTest->u.offNext = pIoTest->u.offNext + pIoTest->cbBlkIo >= pIoTest->offEnd
2850 ? 0
2851 : RT_MIN(pIoTest->offEnd, pIoTest->u.offNext + pIoTest->cbBlkIo);
2852 }
2853 }
2854 pIoReq->pvUser = pvUser;
2855 pIoReq->fOutstanding = true;
2856 }
2857 }
2858 else
2859 rc = VERR_ACCESS_DENIED;
2860
2861 return rc;
2862}
2863
2864static void tstVDIoTestReqComplete(void *pvUser1, void *pvUser2, int rcReq)
2865{
2866 PVDIOREQ pIoReq = (PVDIOREQ)pvUser1;
2867 RTSEMEVENT hEventSem = (RTSEMEVENT)pvUser2;
2868 PVDDISK pDisk = (PVDDISK)pIoReq->pvUser;
2869
2870 LogFlow(("Request %d completed\n", pIoReq->idx));
2871
2872 if (pDisk->pMemDiskVerify)
2873 {
2874 switch (pIoReq->enmTxDir)
2875 {
2876 case VDIOREQTXDIR_READ:
2877 {
2878 RTCritSectEnter(&pDisk->CritSectVerify);
2879 RTSgBufReset(&pIoReq->SgBuf);
2880
2881 if (VDMemDiskCmp(pDisk->pMemDiskVerify, pIoReq->off, pIoReq->cbReq,
2882 &pIoReq->SgBuf))
2883 RTPrintf("Corrupted disk at offset %llu!\n", pIoReq->off);
2884 RTCritSectLeave(&pDisk->CritSectVerify);
2885 }
2886 case VDIOREQTXDIR_WRITE:
2887 {
2888 RTCritSectEnter(&pDisk->CritSectVerify);
2889 RTSgBufReset(&pIoReq->SgBuf);
2890
2891 int rc = VDMemDiskWrite(pDisk->pMemDiskVerify, pIoReq->off, pIoReq->cbReq,
2892 &pIoReq->SgBuf);
2893 AssertRC(rc);
2894 RTCritSectLeave(&pDisk->CritSectVerify);
2895 break;
2896 }
2897 case VDIOREQTXDIR_FLUSH:
2898 case VDIOREQTXDIR_DISCARD:
2899 break;
2900 }
2901 }
2902
2903 ASMAtomicXchgBool(&pIoReq->fOutstanding, false);
2904 RTSemEventSignal(hEventSem);
2905 return;
2906}
2907
2908/**
2909 * Returns the disk handle by name or NULL if not found
2910 *
2911 * @returns Disk handle or NULL if the disk could not be found.
2912 *
2913 * @param pGlob Global test state.
2914 * @param pcszDisk Name of the disk to get.
2915 */
2916static PVDDISK tstVDIoGetDiskByName(PVDTESTGLOB pGlob, const char *pcszDisk)
2917{
2918 PVDDISK pIt = NULL;
2919 bool fFound = false;
2920
2921 LogFlowFunc(("pGlob=%#p pcszDisk=%s\n", pGlob, pcszDisk));
2922
2923 RTListForEach(&pGlob->ListDisks, pIt, VDDISK, ListNode)
2924 {
2925 if (!RTStrCmp(pIt->pszName, pcszDisk))
2926 {
2927 fFound = true;
2928 break;
2929 }
2930 }
2931
2932 LogFlowFunc(("return %#p\n", fFound ? pIt : NULL));
2933 return fFound ? pIt : NULL;
2934}
2935
2936/**
2937 * Returns the I/O pattern handle by name of NULL if not found.
2938 *
2939 * @returns I/O pattern handle or NULL if the pattern could not be found.
2940 *
2941 * @param pGlob Global test state.
2942 * @param pcszName Name of the pattern.
2943 */
2944static PVDPATTERN tstVDIoGetPatternByName(PVDTESTGLOB pGlob, const char *pcszName)
2945{
2946 PVDPATTERN pIt = NULL;
2947 bool fFound = false;
2948
2949 LogFlowFunc(("pGlob=%#p pcszName=%s\n", pGlob, pcszName));
2950
2951 RTListForEach(&pGlob->ListPatterns, pIt, VDPATTERN, ListNode)
2952 {
2953 if (!RTStrCmp(pIt->pszName, pcszName))
2954 {
2955 fFound = true;
2956 break;
2957 }
2958 }
2959
2960 LogFlowFunc(("return %#p\n", fFound ? pIt : NULL));
2961 return fFound ? pIt : NULL;
2962}
2963
2964/**
2965 * Creates a new pattern with the given name and an
2966 * allocated pattern buffer.
2967 *
2968 * @returns Pointer to a new pattern buffer or NULL on failure.
2969 * @param pcszName Name of the pattern.
2970 * @param cbPattern Size of the pattern buffer.
2971 */
2972static PVDPATTERN tstVDIoPatternCreate(const char *pcszName, size_t cbPattern)
2973{
2974 PVDPATTERN pPattern = (PVDPATTERN)RTMemAllocZ(sizeof(VDPATTERN));
2975 char *pszName = RTStrDup(pcszName);
2976 void *pvPattern = RTMemAllocZ(cbPattern);
2977
2978 if (pPattern && pszName && pvPattern)
2979 {
2980 pPattern->pszName = pszName;
2981 pPattern->pvPattern = pvPattern;
2982 pPattern->cbPattern = cbPattern;
2983 }
2984 else
2985 {
2986 if (pPattern)
2987 RTMemFree(pPattern);
2988 if (pszName)
2989 RTStrFree(pszName);
2990 if (pvPattern)
2991 RTMemFree(pvPattern);
2992
2993 pPattern = NULL;
2994 pszName = NULL;
2995 pvPattern = NULL;
2996 }
2997
2998 return pPattern;
2999}
3000
3001static int tstVDIoPatternGetBuffer(PVDPATTERN pPattern, void **ppv, size_t cb)
3002{
3003 AssertPtrReturn(pPattern, VERR_INVALID_POINTER);
3004 AssertPtrReturn(ppv, VERR_INVALID_POINTER);
3005 AssertReturn(cb > 0, VERR_INVALID_PARAMETER);
3006
3007 if (cb > pPattern->cbPattern)
3008 return VERR_INVALID_PARAMETER;
3009
3010 *ppv = pPattern->pvPattern;
3011 return VINF_SUCCESS;
3012}
3013
3014/**
3015 * Skips the characters until the given character is reached.
3016 *
3017 * @returns Start of the string with the given character
3018 * or NULL if the string ended before.
3019 *
3020 * @param psz The string to skip.
3021 * @param ch The character.
3022 */
3023static char *tstVDIoScriptSkipUntil(char *psz, char ch)
3024{
3025 while ( *psz != '\0'
3026 && *psz != ch)
3027 psz++;
3028
3029 return psz;
3030}
3031
3032/**
3033 * Skips the spaces of the current string.
3034 *
3035 * @returns Start of the string with a non space character
3036 * or NULL if the string ended before.
3037 *
3038 * @param psz The string to skip.
3039 */
3040static char *tstVDIoScriptSkipSpace(char *psz)
3041{
3042 while ( *psz != '\0'
3043 && RT_C_IS_SPACE(*psz))
3044 psz++;
3045
3046 return psz;
3047}
3048
3049/**
3050 * Skips all characters until a space is reached of the current
3051 * string.
3052 *
3053 * @returns Start of the string with a space character
3054 * or NULL if the string ended before.
3055 *
3056 * @param psz The string to skip.
3057 */
3058static char *tstVDIoScriptSkipNonSpace(char *psz)
3059{
3060 while ( *psz != '\0'
3061 && !RT_C_IS_SPACE(*psz))
3062 psz++;
3063
3064 return psz;
3065}
3066
3067/**
3068 * Returns true if the first character of the given string
3069 * contains a character marking a line end (comment or \0
3070 * terminator).
3071 *
3072 * @returns true if the line contains no more characters of
3073 * interest and false otherwise.
3074 *
3075 * @param psz The string to check for.
3076 */
3077static bool tstVDIoIsLineEnd(const char *psz)
3078{
3079 return *psz == '\0' || *psz == '#';
3080}
3081
3082/**
3083 * Parses one argument name, value pair.
3084 *
3085 * @returns IPRT status code.
3086 *
3087 * @param pVDScriptAction Script action.
3088 * @param pcszName Argument name.
3089 * @param pcszValue Argument value.
3090 * @param pScriptArg Where to fill in the parsed
3091 * argument.
3092 * @param pfMandatory Where to store whether the argument
3093 * is mandatory.
3094 */
3095static int tstVDIoScriptArgumentParse(PCVDSCRIPTACTION pVDScriptAction, const char *pcszName,
3096 const char *pcszValue, PVDSCRIPTARG pScriptArg, bool *pfMandatory)
3097{
3098 int rc = VERR_NOT_FOUND;
3099
3100 for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
3101 {
3102 if (!RTStrCmp(pVDScriptAction->paArgDesc[i].pcszName, pcszName))
3103 {
3104 rc = VINF_SUCCESS;
3105
3106 switch (pVDScriptAction->paArgDesc[i].enmType)
3107 {
3108 case VDSCRIPTARGTYPE_BOOL:
3109 {
3110 pScriptArg->enmType = VDSCRIPTARGTYPE_BOOL;
3111 if (!RTStrICmp(pcszValue, "yes") || !RTStrICmp(pcszValue, "on"))
3112 pScriptArg->u.fFlag = true;
3113 else if (!RTStrICmp(pcszValue, "no") || !RTStrICmp(pcszValue, "off"))
3114 pScriptArg->u.fFlag = false;
3115 else
3116 {
3117 RTPrintf("Boolean argument malformed '%s'\n", pcszValue);
3118 rc = VERR_INVALID_PARAMETER;
3119 }
3120 break;
3121 }
3122 case VDSCRIPTARGTYPE_SIGNED_NUMBER:
3123 {
3124 pScriptArg->enmType = VDSCRIPTARGTYPE_SIGNED_NUMBER;
3125 AssertMsgFailed(("todo\n"));
3126 break;
3127 }
3128 case VDSCRIPTARGTYPE_STRING:
3129 {
3130 pScriptArg->enmType = VDSCRIPTARGTYPE_STRING;
3131 pScriptArg->u.pcszString = pcszValue;
3132 break;
3133 }
3134 case VDSCRIPTARGTYPE_UNSIGNED_NUMBER:
3135 {
3136 char *pszSuffix = NULL;
3137
3138 pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_NUMBER;
3139 rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.u64);
3140 if (rc == VWRN_TRAILING_CHARS)
3141 {
3142 switch (*pszSuffix)
3143 {
3144 case 'k':
3145 case 'K':
3146 {
3147 pScriptArg->u.u64 *= _1K;
3148 break;
3149 }
3150 case 'm':
3151 case 'M':
3152 {
3153 pScriptArg->u.u64 *= _1M;
3154 break;
3155 }
3156 case 'g':
3157 case 'G':
3158 {
3159 pScriptArg->u.u64 *= _1G;
3160 break;
3161 }
3162 default:
3163 {
3164 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
3165 rc = VERR_INVALID_PARAMETER;
3166 }
3167 }
3168 if (rc != VERR_INVALID_PARAMETER)
3169 rc = VINF_SUCCESS;
3170 }
3171
3172 break;
3173 }
3174 case VDSCRIPTARGTYPE_UNSIGNED_RANGE:
3175 {
3176 char *pszSuffix = NULL;
3177
3178 pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_RANGE;
3179 rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.Range.Start);
3180 if (rc == VWRN_TRAILING_CHARS)
3181 {
3182 if (*pszSuffix != '-')
3183 {
3184 switch (*pszSuffix)
3185 {
3186 case 'k':
3187 case 'K':
3188 {
3189 pScriptArg->u.u64 *= _1K;
3190 break;
3191 }
3192 case 'm':
3193 case 'M':
3194 {
3195 pScriptArg->u.u64 *= _1M;
3196 break;
3197 }
3198 case 'g':
3199 case 'G':
3200 {
3201 pScriptArg->u.u64 *= _1G;
3202 break;
3203 }
3204 default:
3205 {
3206 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
3207 rc = VERR_INVALID_PARAMETER;
3208 }
3209 }
3210 if (RT_SUCCESS(rc))
3211 pszSuffix++;
3212 }
3213
3214 if (*pszSuffix == '-')
3215 {
3216 pszSuffix++;
3217 rc = RTStrToUInt64Ex(pszSuffix, &pszSuffix, 10, &pScriptArg->u.Range.End);
3218 if (rc == VWRN_TRAILING_CHARS)
3219 {
3220 switch (*pszSuffix)
3221 {
3222 case 'k':
3223 case 'K':
3224 {
3225 pScriptArg->u.Range.End *= _1K;
3226 break;
3227 }
3228 case 'm':
3229 case 'M':
3230 {
3231 pScriptArg->u.Range.End *= _1M;
3232 break;
3233 }
3234 case 'g':
3235 case 'G':
3236 {
3237 pScriptArg->u.Range.End *= _1G;
3238 break;
3239 }
3240 default:
3241 {
3242 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
3243 rc = VERR_INVALID_PARAMETER;
3244 }
3245 }
3246 }
3247 }
3248 else
3249 rc = VERR_INVALID_PARAMETER;
3250 }
3251 else
3252 rc = VERR_INVALID_PARAMETER;
3253
3254 if (rc == VERR_INVALID_PARAMETER)
3255 RTPrintf("Invalid range format\n");
3256 break;
3257 }
3258 default:
3259 AssertMsgFailed(("Invalid script argument type\n"));
3260 }
3261
3262 if (RT_SUCCESS(rc))
3263 {
3264 pScriptArg->chId = pVDScriptAction->paArgDesc[i].chId;
3265 *pfMandatory = !!(pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY);
3266 }
3267 break;
3268 }
3269 }
3270
3271 if (rc == VERR_NOT_FOUND)
3272 RTPrintf("Argument '%s' not found\n", pcszName);
3273
3274 return rc;
3275}
3276
3277/**
3278 * Parses the arguments of a action in the script.
3279 *
3280 * @returns IPRT status code.
3281 *
3282 * @param psz Argument string.
3283 * @param pVDScriptAction The script action to parses
3284 * arguments for.
3285 * @param paScriptArgs Where to store the arguments.
3286 * @param pcScriptArgs Where to store the actual number of
3287 * arguments parsed.
3288 */
3289static int tstVDIoScriptArgumentListParse(char *psz, PCVDSCRIPTACTION pVDScriptAction, PVDSCRIPTARG paScriptArgs, unsigned *pcScriptArgs)
3290{
3291 int rc = VINF_SUCCESS;
3292 unsigned cMandatoryArgsReq = 0;
3293 unsigned cScriptArgs = 0;
3294
3295 /* Count the number of mandatory arguments first. */
3296 for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
3297 if (pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY)
3298 cMandatoryArgsReq++;
3299
3300 /* One argument is given in the form name=value. */
3301 *pcScriptArgs = 0;
3302
3303 while ( psz
3304 && !tstVDIoIsLineEnd(psz))
3305 {
3306 const char *pcszName = psz;
3307
3308 psz = tstVDIoScriptSkipUntil(psz, '=');
3309 if (!tstVDIoIsLineEnd(psz))
3310 {
3311 *psz = '\0'; /* Overwrite */
3312 psz++;
3313 const char *pcszValue = psz;
3314
3315 psz = tstVDIoScriptSkipNonSpace(psz);
3316 if (!tstVDIoIsLineEnd(psz))
3317 {
3318 *psz = '\0'; /* Overwrite */
3319 psz++;
3320 psz = tstVDIoScriptSkipSpace(psz);
3321 }
3322
3323 pcszValue = tstVDIoScriptSkipSpace((char *)pcszValue);
3324 if (*pcszValue == '\0')
3325 {
3326 RTPrintf("Value missing for argument '%s'\n", pcszName);
3327 rc = VERR_INVALID_STATE;
3328 break;
3329 }
3330
3331 /* We have the name and value pair now. */
3332 bool fMandatory = false; /* Shut up gcc */
3333 rc = tstVDIoScriptArgumentParse(pVDScriptAction, pcszName, pcszValue, &paScriptArgs[cScriptArgs], &fMandatory);
3334 if (RT_SUCCESS(rc))
3335 {
3336 if (fMandatory)
3337 cMandatoryArgsReq--;
3338 cScriptArgs++;
3339 }
3340 }
3341 else
3342 {
3343 RTPrintf("Argument in invalid form\n");
3344 rc = VERR_INVALID_STATE;
3345 break;
3346 }
3347 }
3348
3349 if ( RT_SUCCESS(rc)
3350 && cMandatoryArgsReq)
3351 {
3352 /* No arguments anymore but there are still mandatory arguments left. */
3353 RTPrintf("There are %u arguments missing for script action '%s\n", pVDScriptAction->pcszAction);
3354 rc = VERR_INVALID_STATE;
3355 }
3356
3357 if (RT_SUCCESS(rc))
3358 *pcScriptArgs = cScriptArgs;
3359
3360 return rc;
3361}
3362
3363/**
3364 * Executes the script pointed to by the given stream.
3365 *
3366 * @returns IPRT status code.
3367 *
3368 * @param pStrm The stream handle of the script.
3369 * @param pGlob Global test data.
3370 */
3371static int tstVDIoScriptExecute(PRTSTREAM pStrm, PVDTESTGLOB pGlob)
3372{
3373 int rc = VINF_SUCCESS;
3374 char abBuffer[0x1000]; /* Current assumption that a line is never longer than 4096 bytes. */
3375 PVDSCRIPTARG paScriptArgs = NULL;
3376 unsigned cScriptArgsMax = 0;
3377
3378 do
3379 {
3380 memset(abBuffer, 0, sizeof(abBuffer));
3381 rc = RTStrmGetLine(pStrm, abBuffer, sizeof(abBuffer));
3382 if (RT_SUCCESS(rc))
3383 {
3384 const char *pcszAction = NULL;
3385 char *psz = abBuffer;
3386
3387 /* Skip space */
3388 psz = tstVDIoScriptSkipSpace(psz);
3389 if (!tstVDIoIsLineEnd(psz))
3390 {
3391 PCVDSCRIPTACTION pVDScriptAction = NULL;
3392
3393 /* Get the action name. */
3394 pcszAction = psz;
3395
3396 psz = tstVDIoScriptSkipNonSpace(psz);
3397 if (!tstVDIoIsLineEnd(psz))
3398 {
3399 Assert(RT_C_IS_SPACE(*psz));
3400 *psz++ = '\0';
3401 }
3402
3403 /* Find the action. */
3404 for (unsigned i = 0; i < g_cScriptActions; i++)
3405 {
3406 if (!RTStrCmp(pcszAction, g_aScriptActions[i].pcszAction))
3407 {
3408 pVDScriptAction = &g_aScriptActions[i];
3409 break;
3410 }
3411 }
3412
3413 if (pVDScriptAction)
3414 {
3415 /* Parse arguments. */
3416 if (cScriptArgsMax < pVDScriptAction->cArgDescs)
3417 {
3418 /* Increase arguments array. */
3419 if (paScriptArgs)
3420 RTMemFree(paScriptArgs);
3421
3422 cScriptArgsMax = pVDScriptAction->cArgDescs;
3423 paScriptArgs = (PVDSCRIPTARG)RTMemAllocZ(cScriptArgsMax * sizeof(VDSCRIPTARG));
3424 }
3425
3426 if (paScriptArgs)
3427 {
3428 unsigned cScriptArgs;
3429
3430 rc = tstVDIoScriptArgumentListParse(psz, pVDScriptAction, paScriptArgs, &cScriptArgs);
3431 if (RT_SUCCESS(rc))
3432 {
3433 /* Execute the handler. */
3434 rc = pVDScriptAction->pfnHandler(pGlob, paScriptArgs, cScriptArgs);
3435 }
3436 }
3437 else
3438 {
3439 RTPrintf("Out of memory while allocating argument array for script action %s\n", pcszAction);
3440 rc = VERR_NO_MEMORY;
3441 }
3442 }
3443 else
3444 {
3445 RTPrintf("Script action %s is not known\n", pcszAction);
3446 rc = VERR_NOT_FOUND;
3447 }
3448 }
3449 /* else empty line, just continue */
3450 }
3451 } while(RT_SUCCESS(rc));
3452
3453 if (rc == VERR_EOF)
3454 {
3455 RTPrintf("Successfully executed I/O script\n");
3456 rc = VINF_SUCCESS;
3457 }
3458 return rc;
3459}
3460
3461/**
3462 * Executes the given I/O script.
3463 *
3464 * @returns nothing.
3465 *
3466 * @param pcszFilename The script to execute.
3467 */
3468static void tstVDIoScriptRun(const char *pcszFilename)
3469{
3470 int rc = VINF_SUCCESS;
3471 PRTSTREAM pScriptStrm; /**< Stream of the script file. */
3472 VDTESTGLOB GlobTest; /**< Global test data. */
3473
3474 memset(&GlobTest, 0, sizeof(VDTESTGLOB));
3475 RTListInit(&GlobTest.ListFiles);
3476 RTListInit(&GlobTest.ListDisks);
3477 RTListInit(&GlobTest.ListPatterns);
3478
3479 rc = RTStrmOpen(pcszFilename, "r", &pScriptStrm);
3480 if (RT_SUCCESS(rc))
3481 {
3482 /* Init global test data. */
3483 GlobTest.VDIfError.pfnError = tstVDError;
3484 GlobTest.VDIfError.pfnMessage = tstVDMessage;
3485
3486 rc = VDInterfaceAdd(&GlobTest.VDIfError.Core, "tstVDIo_VDIError", VDINTERFACETYPE_ERROR,
3487 NULL, sizeof(VDINTERFACEERROR), &GlobTest.pInterfacesDisk);
3488 AssertRC(rc);
3489
3490 GlobTest.VDIfIo.pfnOpen = tstVDIoFileOpen;
3491 GlobTest.VDIfIo.pfnClose = tstVDIoFileClose;
3492 GlobTest.VDIfIo.pfnDelete = tstVDIoFileDelete;
3493 GlobTest.VDIfIo.pfnMove = tstVDIoFileMove;
3494 GlobTest.VDIfIo.pfnGetFreeSpace = tstVDIoFileGetFreeSpace;
3495 GlobTest.VDIfIo.pfnGetModificationTime = tstVDIoFileGetModificationTime;
3496 GlobTest.VDIfIo.pfnGetSize = tstVDIoFileGetSize;
3497 GlobTest.VDIfIo.pfnSetSize = tstVDIoFileSetSize;
3498 GlobTest.VDIfIo.pfnWriteSync = tstVDIoFileWriteSync;
3499 GlobTest.VDIfIo.pfnReadSync = tstVDIoFileReadSync;
3500 GlobTest.VDIfIo.pfnFlushSync = tstVDIoFileFlushSync;
3501 GlobTest.VDIfIo.pfnReadAsync = tstVDIoFileReadAsync;
3502 GlobTest.VDIfIo.pfnWriteAsync = tstVDIoFileWriteAsync;
3503 GlobTest.VDIfIo.pfnFlushAsync = tstVDIoFileFlushAsync;
3504
3505 rc = VDInterfaceAdd(&GlobTest.VDIfIo.Core, "tstVDIo_VDIIo", VDINTERFACETYPE_IO,
3506 &GlobTest, sizeof(VDINTERFACEIO), &GlobTest.pInterfacesImages);
3507 AssertRC(rc);
3508
3509 /* Init I/O backend. */
3510 rc = VDIoBackendMemCreate(&GlobTest.pIoBackend);
3511 if (RT_SUCCESS(rc))
3512 {
3513 /* Execute the script. */
3514 rc = tstVDIoScriptExecute(pScriptStrm, &GlobTest);
3515 if (RT_FAILURE(rc))
3516 {
3517 RTPrintf("Executing the script stream failed rc=%Rrc\n", rc);
3518 }
3519 VDIoBackendMemDestroy(GlobTest.pIoBackend);
3520 }
3521 else
3522 RTPrintf("Creating the I/O backend failed rc=%Rrc\n");
3523
3524 RTStrmClose(pScriptStrm);
3525 }
3526 else
3527 RTPrintf("Opening script failed rc=%Rrc\n", rc);
3528}
3529
3530/**
3531 * Shows help message.
3532 */
3533static void printUsage(void)
3534{
3535 RTPrintf("Usage:\n"
3536 "--script <filename> Script to execute\n");
3537}
3538
3539static const RTGETOPTDEF g_aOptions[] =
3540{
3541 { "--script", 's', RTGETOPT_REQ_STRING }
3542};
3543
3544int main(int argc, char *argv[])
3545{
3546 RTR3InitExe(argc, &argv, 0);
3547 int rc;
3548 RTGETOPTUNION ValueUnion;
3549 RTGETOPTSTATE GetState;
3550 char c;
3551
3552 if (argc != 3)
3553 {
3554 printUsage();
3555 return RTEXITCODE_FAILURE;
3556 }
3557
3558 rc = VDInit();
3559 if (RT_FAILURE(rc))
3560 return RTEXITCODE_FAILURE;
3561
3562 RTGetOptInit(&GetState, argc, argv, g_aOptions,
3563 RT_ELEMENTS(g_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
3564
3565 while ( RT_SUCCESS(rc)
3566 && (c = RTGetOpt(&GetState, &ValueUnion)))
3567 {
3568 switch (c)
3569 {
3570 case 's':
3571 tstVDIoScriptRun(ValueUnion.psz);
3572 break;
3573 default:
3574 printUsage();
3575 }
3576 }
3577
3578 rc = VDShutdown();
3579 if (RT_FAILURE(rc))
3580 RTPrintf("tstVDIo: unloading backends failed! rc=%Rrc\n", rc);
3581
3582 return RTEXITCODE_SUCCESS;
3583}
3584
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