VirtualBox

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

Last change on this file since 96238 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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

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