VirtualBox

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

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

tstVDIo: New testcase for image cloning

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

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