VirtualBox

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

最後變更 在這個檔案從44901是 44529,由 vboxsync 提交於 12 年 前

header (C) fixes

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

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