VirtualBox

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

最後變更 在這個檔案從40709是 40709,由 vboxsync 提交於 13 年 前

Storage/tstVDIo: Add file access statistics and parameters for the ignore flush flag

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

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