VirtualBox

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

Last change on this file since 97271 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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