VirtualBox

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

Last change on this file since 102493 was 100079, checked in by vboxsync, 18 months ago

tstVDIo.cpp: updating VDCopyEx parameters: bugref:5995

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 99.5 KB
Line 
1/* $Id: tstVDIo.cpp 100079 2023-06-06 07:10:47Z vboxsync $ */
2/** @file
3 * VBox HDD container test utility - I/O replay.
4 */
5
6/*
7 * Copyright (C) 2011-2023 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 bool fSplit = false;
570
571 const char *pcszDisk = paScriptArgs[0].psz;
572 if (!RTStrICmp(paScriptArgs[1].psz, "base"))
573 fBase = true;
574 else if (!RTStrICmp(paScriptArgs[1].psz, "diff"))
575 fBase = false;
576 else
577 {
578 RTPrintf("Invalid image mode '%s' given\n", paScriptArgs[1].psz);
579 rc = VERR_INVALID_PARAMETER;
580 }
581 const char *pcszImage = paScriptArgs[2].psz;
582 if (!RTStrICmp(paScriptArgs[3].psz, "fixed"))
583 fDynamic = false;
584 else if (!RTStrICmp(paScriptArgs[3].psz, "dynamic"))
585 fDynamic = true;
586 else if (!RTStrICmp(paScriptArgs[3].psz, "vmdk-dynamic-split"))
587 fSplit = true;
588 else if (!RTStrICmp(paScriptArgs[3].psz, "vmdk-fixed-split"))
589 {
590 fDynamic = false;
591 fSplit = true;
592 }
593 else
594 {
595 RTPrintf("Invalid image type '%s' given\n", paScriptArgs[3].psz);
596 rc = VERR_INVALID_PARAMETER;
597 }
598 const char *pcszBackend = paScriptArgs[4].psz;
599 uint64_t cbSize = paScriptArgs[5].u64;
600 bool fIgnoreFlush = paScriptArgs[6].f;
601 bool fHonorSame = paScriptArgs[7].f;
602
603 if (RT_SUCCESS(rc))
604 {
605 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
606 if (pDisk)
607 {
608 unsigned fOpenFlags = VD_OPEN_FLAGS_ASYNC_IO;
609 unsigned fImageFlags = VD_IMAGE_FLAGS_NONE;
610
611 if (!fDynamic)
612 fImageFlags |= VD_IMAGE_FLAGS_FIXED;
613
614 if (fIgnoreFlush)
615 fOpenFlags |= VD_OPEN_FLAGS_IGNORE_FLUSH;
616
617 if (fHonorSame)
618 fOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
619
620 if (fSplit)
621 fImageFlags |= VD_VMDK_IMAGE_FLAGS_SPLIT_2G;
622
623 if (fBase)
624 rc = VDCreateBase(pDisk->pVD, pcszBackend, pcszImage, cbSize, fImageFlags, NULL,
625 &pDisk->PhysGeom, &pDisk->LogicalGeom,
626 NULL, fOpenFlags, pGlob->pInterfacesImages, NULL);
627 else
628 rc = VDCreateDiff(pDisk->pVD, pcszBackend, pcszImage, fImageFlags, NULL, NULL, NULL,
629 fOpenFlags, pGlob->pInterfacesImages, NULL);
630 }
631 else
632 rc = VERR_NOT_FOUND;
633 }
634
635 return rc;
636}
637
638static DECLCALLBACK(int) vdScriptHandlerOpen(PVDSCRIPTARG paScriptArgs, void *pvUser)
639{
640 int rc = VINF_SUCCESS;
641 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
642 PVDDISK pDisk = NULL;
643
644 const char *pcszDisk = paScriptArgs[0].psz;
645 const char *pcszImage = paScriptArgs[1].psz;
646 const char *pcszBackend = paScriptArgs[2].psz;
647 bool fShareable = paScriptArgs[3].f;
648 bool fReadonly = paScriptArgs[4].f;
649 bool fAsyncIo = paScriptArgs[5].f;
650 bool fDiscard = paScriptArgs[6].f;
651 bool fIgnoreFlush = paScriptArgs[7].f;
652 bool fHonorSame = paScriptArgs[8].f;
653
654 if (RT_SUCCESS(rc))
655 {
656 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
657 if (pDisk)
658 {
659 unsigned fOpenFlags = 0;
660
661 if (fAsyncIo)
662 fOpenFlags |= VD_OPEN_FLAGS_ASYNC_IO;
663 if (fShareable)
664 fOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
665 if (fReadonly)
666 fOpenFlags |= VD_OPEN_FLAGS_READONLY;
667 if (fDiscard)
668 fOpenFlags |= VD_OPEN_FLAGS_DISCARD;
669 if (fIgnoreFlush)
670 fOpenFlags |= VD_OPEN_FLAGS_IGNORE_FLUSH;
671 if (fHonorSame)
672 fOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
673
674 rc = VDOpen(pDisk->pVD, pcszBackend, pcszImage, fOpenFlags, pGlob->pInterfacesImages);
675 }
676 else
677 rc = VERR_NOT_FOUND;
678 }
679
680 return rc;
681}
682
683/**
684 * Returns the speed in KB/s from the amount of and the time in nanoseconds it
685 * took to complete the test.
686 *
687 * @returns Speed in KB/s
688 * @param cbIo Size of the I/O test
689 * @param tsNano Time in nanoseconds it took to complete the test.
690 */
691static uint64_t tstVDIoGetSpeedKBs(uint64_t cbIo, uint64_t tsNano)
692{
693 /* Seen on one of the testboxes, avoid division by 0 below. */
694 if (tsNano == 0)
695 return 0;
696
697 /*
698 * Blow up the value until we can do the calculation without getting 0 as
699 * a result.
700 */
701 uint64_t cbIoTemp = cbIo;
702 unsigned cRounds = 0;
703 while (cbIoTemp < tsNano)
704 {
705 cbIoTemp *= 1000;
706 cRounds++;
707 }
708
709 uint64_t uSpeedKBs = ((cbIoTemp / tsNano) * RT_NS_1SEC) / 1024;
710
711 while (cRounds-- > 0)
712 uSpeedKBs /= 1000;
713
714 return uSpeedKBs;
715}
716
717static DECLCALLBACK(int) vdScriptHandlerIo(PVDSCRIPTARG paScriptArgs, void *pvUser)
718{
719 int rc = VINF_SUCCESS;
720 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
721 bool fRandomAcc = false;
722 PVDDISK pDisk = NULL;
723 PVDPATTERN pPattern = NULL;
724
725 const char *pcszDisk = paScriptArgs[0].psz;
726 bool fAsync = paScriptArgs[1].f;
727 unsigned cMaxReqs = (unsigned)paScriptArgs[2].u64;
728 if (!RTStrICmp(paScriptArgs[3].psz, "seq"))
729 fRandomAcc = false;
730 else if (!RTStrICmp(paScriptArgs[3].psz, "rnd"))
731 fRandomAcc = true;
732 else
733 {
734 RTPrintf("Invalid access mode '%s'\n", paScriptArgs[3].psz);
735 rc = VERR_INVALID_PARAMETER;
736 }
737 uint64_t cbBlkSize = paScriptArgs[4].u64;
738 uint64_t offStart = paScriptArgs[5].u64;
739 uint64_t offEnd = paScriptArgs[6].u64;
740 uint64_t cbIo = paScriptArgs[7].u64;
741 uint8_t uWriteChance = (uint8_t)paScriptArgs[8].u64;
742 const char *pcszPattern = paScriptArgs[9].psz;
743
744 if ( RT_SUCCESS(rc)
745 && fAsync
746 && !cMaxReqs)
747 rc = VERR_INVALID_PARAMETER;
748
749 if (RT_SUCCESS(rc))
750 {
751 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
752 if (!pDisk)
753 rc = VERR_NOT_FOUND;
754 }
755
756 if (RT_SUCCESS(rc))
757 {
758 /* Set defaults if not set by the user. */
759 if (offStart == 0 && offEnd == 0)
760 {
761 offEnd = VDGetSize(pDisk->pVD, VD_LAST_IMAGE);
762 if (offEnd == 0)
763 return VERR_INVALID_STATE;
764 }
765
766 if (!cbIo)
767 cbIo = offEnd;
768 }
769
770 if ( RT_SUCCESS(rc)
771 && RTStrCmp(pcszPattern, "none"))
772 {
773 pPattern = tstVDIoGetPatternByName(pGlob, pcszPattern);
774 if (!pPattern)
775 rc = VERR_NOT_FOUND;
776 }
777
778 if (RT_SUCCESS(rc))
779 {
780 VDIOTEST IoTest;
781
782 RTTestSub(pGlob->hTest, "Basic I/O");
783 rc = tstVDIoTestInit(&IoTest, pGlob, fRandomAcc, 5, cbIo, cbBlkSize, offStart, offEnd, uWriteChance, pPattern);
784 if (RT_SUCCESS(rc))
785 {
786 PTSTVDIOREQ paIoReq = NULL;
787 unsigned cMaxTasksOutstanding = fAsync ? cMaxReqs : 1;
788 RTSEMEVENT EventSem;
789
790 rc = RTSemEventCreate(&EventSem);
791 paIoReq = (PTSTVDIOREQ)RTMemAllocZ(cMaxTasksOutstanding * sizeof(TSTVDIOREQ));
792 if (paIoReq && RT_SUCCESS(rc))
793 {
794 uint64_t NanoTS = RTTimeNanoTS();
795
796 /* Init requests. */
797 for (unsigned i = 0; i < cMaxTasksOutstanding; i++)
798 {
799 paIoReq[i].idx = i;
800 paIoReq[i].pvBufRead = RTMemAlloc(cbBlkSize);
801 if (!paIoReq[i].pvBufRead)
802 {
803 rc = VERR_NO_MEMORY;
804 break;
805 }
806 }
807
808 while ( tstVDIoTestRunning(&IoTest)
809 && RT_SUCCESS(rc))
810 {
811 bool fTasksOutstanding = false;
812 unsigned idx = 0;
813
814 /* Submit all idling requests. */
815 while ( idx < cMaxTasksOutstanding
816 && tstVDIoTestRunning(&IoTest))
817 {
818 if (!tstVDIoTestReqOutstanding(&paIoReq[idx]))
819 {
820 rc = tstVDIoTestReqInit(&IoTest, &paIoReq[idx], pDisk);
821 AssertRC(rc);
822
823 if (RT_SUCCESS(rc))
824 {
825 if (!fAsync)
826 {
827 switch (paIoReq[idx].enmTxDir)
828 {
829 case TSTVDIOREQTXDIR_READ:
830 {
831 rc = VDRead(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].aSegs[0].pvSeg, paIoReq[idx].cbReq);
832
833 if (RT_SUCCESS(rc)
834 && pDisk->pMemDiskVerify)
835 {
836 RTSGBUF SgBuf;
837 RTSgBufInit(&SgBuf, &paIoReq[idx].aSegs[0], paIoReq[idx].cSegs);
838
839 if (VDMemDiskCmp(pDisk->pMemDiskVerify, paIoReq[idx].off, paIoReq[idx].cbReq, &SgBuf))
840 {
841 RTTestFailed(pGlob->hTest, "Corrupted disk at offset %llu!\n", paIoReq[idx].off);
842 rc = VERR_INVALID_STATE;
843 }
844 }
845 break;
846 }
847 case TSTVDIOREQTXDIR_WRITE:
848 {
849 rc = VDWrite(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].aSegs[0].pvSeg, paIoReq[idx].cbReq);
850
851 if (RT_SUCCESS(rc)
852 && pDisk->pMemDiskVerify)
853 {
854 RTSGBUF SgBuf;
855 RTSgBufInit(&SgBuf, &paIoReq[idx].aSegs[0], paIoReq[idx].cSegs);
856 rc = VDMemDiskWrite(pDisk->pMemDiskVerify, paIoReq[idx].off, paIoReq[idx].cbReq, &SgBuf);
857 }
858 break;
859 }
860 case TSTVDIOREQTXDIR_FLUSH:
861 {
862 rc = VDFlush(pDisk->pVD);
863 break;
864 }
865 case TSTVDIOREQTXDIR_DISCARD:
866 AssertMsgFailed(("Invalid\n"));
867 }
868
869 ASMAtomicXchgBool(&paIoReq[idx].fOutstanding, false);
870 if (RT_SUCCESS(rc))
871 idx++;
872 }
873 else
874 {
875 LogFlow(("Queuing request %d\n", idx));
876 switch (paIoReq[idx].enmTxDir)
877 {
878 case TSTVDIOREQTXDIR_READ:
879 {
880 rc = VDAsyncRead(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].cbReq, &paIoReq[idx].SgBuf,
881 tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
882 break;
883 }
884 case TSTVDIOREQTXDIR_WRITE:
885 {
886 rc = VDAsyncWrite(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].cbReq, &paIoReq[idx].SgBuf,
887 tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
888 break;
889 }
890 case TSTVDIOREQTXDIR_FLUSH:
891 {
892 rc = VDAsyncFlush(pDisk->pVD, tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
893 break;
894 }
895 case TSTVDIOREQTXDIR_DISCARD:
896 AssertMsgFailed(("Invalid\n"));
897 }
898
899 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
900 {
901 idx++;
902 fTasksOutstanding = true;
903 rc = VINF_SUCCESS;
904 }
905 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
906 {
907 LogFlow(("Request %d completed\n", idx));
908 switch (paIoReq[idx].enmTxDir)
909 {
910 case TSTVDIOREQTXDIR_READ:
911 {
912 if (pDisk->pMemDiskVerify)
913 {
914 RTCritSectEnter(&pDisk->CritSectVerify);
915 RTSgBufReset(&paIoReq[idx].SgBuf);
916
917 if (VDMemDiskCmp(pDisk->pMemDiskVerify, paIoReq[idx].off, paIoReq[idx].cbReq,
918 &paIoReq[idx].SgBuf))
919 {
920 RTTestFailed(pGlob->hTest, "Corrupted disk at offset %llu!\n", paIoReq[idx].off);
921 rc = VERR_INVALID_STATE;
922 }
923 RTCritSectLeave(&pDisk->CritSectVerify);
924 }
925 break;
926 }
927 case TSTVDIOREQTXDIR_WRITE:
928 {
929 if (pDisk->pMemDiskVerify)
930 {
931 RTCritSectEnter(&pDisk->CritSectVerify);
932 RTSgBufReset(&paIoReq[idx].SgBuf);
933
934 rc = VDMemDiskWrite(pDisk->pMemDiskVerify, paIoReq[idx].off, paIoReq[idx].cbReq,
935 &paIoReq[idx].SgBuf);
936 RTCritSectLeave(&pDisk->CritSectVerify);
937 }
938 break;
939 }
940 case TSTVDIOREQTXDIR_FLUSH:
941 break;
942 case TSTVDIOREQTXDIR_DISCARD:
943 AssertMsgFailed(("Invalid\n"));
944 }
945
946 ASMAtomicXchgBool(&paIoReq[idx].fOutstanding, false);
947 if (rc != VERR_INVALID_STATE)
948 rc = VINF_SUCCESS;
949 }
950 }
951
952 if (RT_FAILURE(rc))
953 RTPrintf("Error submitting task %u rc=%Rrc\n", paIoReq[idx].idx, rc);
954 }
955 }
956 }
957
958 /* Wait for a request to complete. */
959 if ( fAsync
960 && fTasksOutstanding)
961 {
962 rc = RTSemEventWait(EventSem, RT_INDEFINITE_WAIT);
963 AssertRC(rc);
964 }
965 }
966
967 /* Cleanup, wait for all tasks to complete. */
968 while (fAsync)
969 {
970 unsigned idx = 0;
971 bool fAllIdle = true;
972
973 while (idx < cMaxTasksOutstanding)
974 {
975 if (tstVDIoTestReqOutstanding(&paIoReq[idx]))
976 {
977 fAllIdle = false;
978 break;
979 }
980 idx++;
981 }
982
983 if (!fAllIdle)
984 {
985 rc = RTSemEventWait(EventSem, 100);
986 Assert(RT_SUCCESS(rc) || rc == VERR_TIMEOUT);
987 }
988 else
989 break;
990 }
991
992 NanoTS = RTTimeNanoTS() - NanoTS;
993 uint64_t SpeedKBs = tstVDIoGetSpeedKBs(cbIo, NanoTS);
994 RTTestValue(pGlob->hTest, "Throughput", SpeedKBs, RTTESTUNIT_KILOBYTES_PER_SEC);
995
996 for (unsigned i = 0; i < cMaxTasksOutstanding; i++)
997 {
998 if (paIoReq[i].pvBufRead)
999 RTMemFree(paIoReq[i].pvBufRead);
1000 }
1001
1002 RTSemEventDestroy(EventSem);
1003 RTMemFree(paIoReq);
1004 }
1005 else
1006 {
1007 if (paIoReq)
1008 RTMemFree(paIoReq);
1009 if (RT_SUCCESS(rc))
1010 RTSemEventDestroy(EventSem);
1011 rc = VERR_NO_MEMORY;
1012 }
1013
1014 tstVDIoTestDestroy(&IoTest);
1015 }
1016 RTTestSubDone(pGlob->hTest);
1017 }
1018
1019 return rc;
1020}
1021
1022static DECLCALLBACK(int) vdScriptHandlerFlush(PVDSCRIPTARG paScriptArgs, void *pvUser)
1023{
1024 int rc = VINF_SUCCESS;
1025 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1026 PVDDISK pDisk = NULL;
1027 const char *pcszDisk = paScriptArgs[0].psz;
1028 bool fAsync = paScriptArgs[1].f;
1029
1030 if (RT_SUCCESS(rc))
1031 {
1032 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1033 if (!pDisk)
1034 rc = VERR_NOT_FOUND;
1035 else if (fAsync)
1036 {
1037 TSTVDIOREQ IoReq;
1038 RTSEMEVENT EventSem;
1039
1040 rc = RTSemEventCreate(&EventSem);
1041 if (RT_SUCCESS(rc))
1042 {
1043 memset(&IoReq, 0, sizeof(TSTVDIOREQ));
1044 IoReq.enmTxDir = TSTVDIOREQTXDIR_FLUSH;
1045 IoReq.pvUser = pDisk;
1046 IoReq.idx = 0;
1047 rc = VDAsyncFlush(pDisk->pVD, tstVDIoTestReqComplete, &IoReq, EventSem);
1048 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1049 {
1050 rc = RTSemEventWait(EventSem, RT_INDEFINITE_WAIT);
1051 AssertRC(rc);
1052 }
1053 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
1054 rc = VINF_SUCCESS;
1055
1056 RTSemEventDestroy(EventSem);
1057 }
1058 }
1059 else
1060 rc = VDFlush(pDisk->pVD);
1061 }
1062
1063 return rc;
1064}
1065
1066static DECLCALLBACK(int) vdScriptHandlerMerge(PVDSCRIPTARG paScriptArgs, void *pvUser)
1067{
1068 int rc = VINF_SUCCESS;
1069 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1070 PVDDISK pDisk = NULL;
1071 const char *pcszDisk = paScriptArgs[0].psz;
1072 unsigned nImageFrom = paScriptArgs[1].u32;
1073 unsigned nImageTo = paScriptArgs[2].u32;
1074
1075 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1076 if (!pDisk)
1077 rc = VERR_NOT_FOUND;
1078 else
1079 {
1080 /** @todo Provide progress interface to test that cancelation
1081 * doesn't corrupt the data.
1082 */
1083 rc = VDMerge(pDisk->pVD, nImageFrom, nImageTo, NULL);
1084 }
1085
1086 return rc;
1087}
1088
1089static DECLCALLBACK(int) vdScriptHandlerCompact(PVDSCRIPTARG paScriptArgs, void *pvUser)
1090{
1091 int rc = VINF_SUCCESS;
1092 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1093 PVDDISK pDisk = NULL;
1094 const char *pcszDisk = paScriptArgs[0].psz;
1095 unsigned nImage = paScriptArgs[1].u32;
1096
1097 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1098 if (!pDisk)
1099 rc = VERR_NOT_FOUND;
1100 else
1101 {
1102 /** @todo Provide progress interface to test that cancelation
1103 * doesn't corrupt the data.
1104 */
1105 rc = VDCompact(pDisk->pVD, nImage, NULL);
1106 }
1107
1108 return rc;
1109}
1110
1111static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDSCRIPTARG paScriptArgs, void *pvUser)
1112{
1113 int rc = VINF_SUCCESS;
1114 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1115 PVDDISK pDisk = NULL;
1116 const char *pcszDisk = paScriptArgs[0].psz;
1117 bool fAsync = paScriptArgs[1].f;
1118 const char *pcszRanges = paScriptArgs[2].psz;
1119
1120 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1121 if (!pDisk)
1122 rc = VERR_NOT_FOUND;
1123 else
1124 {
1125 unsigned cRanges = 0;
1126 PRTRANGE paRanges = NULL;
1127
1128 /*
1129 * Parse the range string which should look like this:
1130 * n,off1,cb1,off2,cb2,...
1131 *
1132 * <n> gives the number of ranges in the string and every off<i>,cb<i>
1133 * pair afterwards is a start offset + number of bytes to discard entry.
1134 */
1135 do
1136 {
1137 rc = RTStrToUInt32Ex(pcszRanges, (char **)&pcszRanges, 10, &cRanges);
1138 if (RT_FAILURE(rc) && (rc != VWRN_TRAILING_CHARS))
1139 break;
1140
1141 if (!cRanges)
1142 {
1143 rc = VERR_INVALID_PARAMETER;
1144 break;
1145 }
1146
1147 paRanges = (PRTRANGE)RTMemAllocZ(cRanges * sizeof(RTRANGE));
1148 if (!paRanges)
1149 {
1150 rc = VERR_NO_MEMORY;
1151 break;
1152 }
1153
1154 if (*pcszRanges != ',')
1155 {
1156 rc = VERR_INVALID_PARAMETER;
1157 break;
1158 }
1159
1160 pcszRanges++;
1161
1162 /* Retrieve each pair from the string. */
1163 for (unsigned i = 0; i < cRanges; i++)
1164 {
1165 uint64_t off;
1166 uint32_t cb;
1167
1168 rc = RTStrToUInt64Ex(pcszRanges, (char **)&pcszRanges, 10, &off);
1169 if (RT_FAILURE(rc) && (rc != VWRN_TRAILING_CHARS))
1170 break;
1171
1172 if (*pcszRanges != ',')
1173 {
1174 switch (*pcszRanges)
1175 {
1176 case 'k':
1177 case 'K':
1178 {
1179 off *= _1K;
1180 break;
1181 }
1182 case 'm':
1183 case 'M':
1184 {
1185 off *= _1M;
1186 break;
1187 }
1188 case 'g':
1189 case 'G':
1190 {
1191 off *= _1G;
1192 break;
1193 }
1194 default:
1195 {
1196 RTPrintf("Invalid size suffix '%s'\n", pcszRanges);
1197 rc = VERR_INVALID_PARAMETER;
1198 }
1199 }
1200 if (RT_SUCCESS(rc))
1201 pcszRanges++;
1202 }
1203
1204 if (*pcszRanges != ',')
1205 {
1206 rc = VERR_INVALID_PARAMETER;
1207 break;
1208 }
1209
1210 pcszRanges++;
1211
1212 rc = RTStrToUInt32Ex(pcszRanges, (char **)&pcszRanges, 10, &cb);
1213 if (RT_FAILURE(rc) && (rc != VWRN_TRAILING_CHARS))
1214 break;
1215
1216 if (*pcszRanges != ',')
1217 {
1218 switch (*pcszRanges)
1219 {
1220 case 'k':
1221 case 'K':
1222 {
1223 cb *= _1K;
1224 break;
1225 }
1226 case 'm':
1227 case 'M':
1228 {
1229 cb *= _1M;
1230 break;
1231 }
1232 case 'g':
1233 case 'G':
1234 {
1235 cb *= _1G;
1236 break;
1237 }
1238 default:
1239 {
1240 RTPrintf("Invalid size suffix '%s'\n", pcszRanges);
1241 rc = VERR_INVALID_PARAMETER;
1242 }
1243 }
1244 if (RT_SUCCESS(rc))
1245 pcszRanges++;
1246 }
1247
1248 if ( *pcszRanges != ','
1249 && !(i == cRanges - 1 && *pcszRanges == '\0'))
1250 {
1251 rc = VERR_INVALID_PARAMETER;
1252 break;
1253 }
1254
1255 pcszRanges++;
1256
1257 paRanges[i].offStart = off;
1258 paRanges[i].cbRange = cb;
1259 }
1260 } while (0);
1261
1262 if (RT_SUCCESS(rc))
1263 {
1264 if (!fAsync)
1265 rc = VDDiscardRanges(pDisk->pVD, paRanges, cRanges);
1266 else
1267 {
1268 TSTVDIOREQ IoReq;
1269 RTSEMEVENT EventSem;
1270
1271 rc = RTSemEventCreate(&EventSem);
1272 if (RT_SUCCESS(rc))
1273 {
1274 memset(&IoReq, 0, sizeof(TSTVDIOREQ));
1275 IoReq.enmTxDir = TSTVDIOREQTXDIR_FLUSH;
1276 IoReq.pvUser = pDisk;
1277 IoReq.idx = 0;
1278 rc = VDAsyncDiscardRanges(pDisk->pVD, paRanges, cRanges, tstVDIoTestReqComplete, &IoReq, EventSem);
1279 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1280 {
1281 rc = RTSemEventWait(EventSem, RT_INDEFINITE_WAIT);
1282 AssertRC(rc);
1283 }
1284 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
1285 rc = VINF_SUCCESS;
1286
1287 RTSemEventDestroy(EventSem);
1288 }
1289 }
1290
1291 if ( RT_SUCCESS(rc)
1292 && pDisk->pMemDiskVerify)
1293 {
1294 for (unsigned i = 0; i < cRanges; i++)
1295 {
1296 void *pv = RTMemAllocZ(paRanges[i].cbRange);
1297 if (pv)
1298 {
1299 RTSGSEG SgSeg;
1300 RTSGBUF SgBuf;
1301
1302 SgSeg.pvSeg = pv;
1303 SgSeg.cbSeg = paRanges[i].cbRange;
1304 RTSgBufInit(&SgBuf, &SgSeg, 1);
1305 rc = VDMemDiskWrite(pDisk->pMemDiskVerify, paRanges[i].offStart, paRanges[i].cbRange, &SgBuf);
1306 RTMemFree(pv);
1307 }
1308 else
1309 {
1310 rc = VERR_NO_MEMORY;
1311 break;
1312 }
1313 }
1314 }
1315 }
1316
1317 if (paRanges)
1318 RTMemFree(paRanges);
1319 }
1320
1321 return rc;
1322}
1323
1324static DECLCALLBACK(int) vdScriptHandlerCopy(PVDSCRIPTARG paScriptArgs, void *pvUser)
1325{
1326 int rc = VINF_SUCCESS;
1327 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1328 PVDDISK pDiskFrom = NULL;
1329 PVDDISK pDiskTo = NULL;
1330 const char *pcszDiskFrom = paScriptArgs[0].psz;
1331 const char *pcszDiskTo = paScriptArgs[1].psz;
1332 unsigned nImageFrom = paScriptArgs[2].u32;
1333 const char *pcszBackend = paScriptArgs[3].psz;
1334 const char *pcszFilename = paScriptArgs[4].psz;
1335 bool fMoveByRename = paScriptArgs[5].f;
1336 uint64_t cbSize = paScriptArgs[6].u64;
1337 unsigned nImageFromSame = paScriptArgs[7].u32;
1338 unsigned nImageToSame = paScriptArgs[8].u32;
1339
1340 pDiskFrom = tstVDIoGetDiskByName(pGlob, pcszDiskFrom);
1341 pDiskTo = tstVDIoGetDiskByName(pGlob, pcszDiskTo);
1342 if (!pDiskFrom || !pDiskTo)
1343 rc = VERR_NOT_FOUND;
1344 else
1345 {
1346 /** @todo Provide progress interface to test that cancelation
1347 * works as intended.
1348 */
1349 rc = VDCopyEx(pDiskFrom->pVD, nImageFrom, pDiskTo->pVD, VD_LAST_IMAGE, pcszBackend, pcszFilename,
1350 fMoveByRename, cbSize, nImageFromSame, nImageToSame,
1351 VD_IMAGE_FLAGS_NONE, NULL, VD_OPEN_FLAGS_ASYNC_IO,
1352 NULL, pGlob->pInterfacesImages, NULL);
1353 }
1354
1355 return rc;
1356}
1357
1358static DECLCALLBACK(int) vdScriptHandlerClose(PVDSCRIPTARG paScriptArgs, void *pvUser)
1359{
1360 int rc = VINF_SUCCESS;
1361 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1362 bool fAll = false;
1363 bool fDelete = false;
1364 const char *pcszDisk = NULL;
1365 PVDDISK pDisk = NULL;
1366
1367 pcszDisk = paScriptArgs[0].psz;
1368 if (!RTStrICmp(paScriptArgs[1].psz, "all"))
1369 fAll = true;
1370 else if (!RTStrICmp(paScriptArgs[1].psz, "single"))
1371 fAll = false;
1372 else
1373 {
1374 RTPrintf("Invalid mode '%s' given\n", paScriptArgs[1].psz);
1375 rc = VERR_INVALID_PARAMETER;
1376 }
1377 fDelete = paScriptArgs[2].f;
1378
1379 if ( fAll
1380 && fDelete)
1381 {
1382 RTPrintf("mode=all doesn't work with delete=yes\n");
1383 rc = VERR_INVALID_PARAMETER;
1384 }
1385
1386 if (RT_SUCCESS(rc))
1387 {
1388 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1389 if (pDisk)
1390 {
1391 if (fAll)
1392 rc = VDCloseAll(pDisk->pVD);
1393 else
1394 rc = VDClose(pDisk->pVD, fDelete);
1395 }
1396 else
1397 rc = VERR_NOT_FOUND;
1398 }
1399 return rc;
1400}
1401
1402
1403static DECLCALLBACK(int) vdScriptHandlerPrintFileSize(PVDSCRIPTARG paScriptArgs, void *pvUser)
1404{
1405 int rc = VINF_SUCCESS;
1406 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1407 PVDDISK pDisk = NULL;
1408 const char *pcszDisk = paScriptArgs[0].psz;
1409 uint32_t nImage = paScriptArgs[1].u32;
1410
1411 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1412 if (pDisk)
1413 RTPrintf("%s: size of image %u is %llu\n", pcszDisk, nImage, VDGetFileSize(pDisk->pVD, nImage));
1414 else
1415 rc = VERR_NOT_FOUND;
1416
1417 return rc;
1418}
1419
1420
1421static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDSCRIPTARG paScriptArgs, void *pvUser)
1422{
1423 int rc = VINF_SUCCESS;
1424 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1425 PVDDISK pDisk = NULL;
1426 const char *pcszDisk = paScriptArgs[0].psz;
1427 const char *pcszIoLog = paScriptArgs[1].psz;
1428 size_t cbBuf = 0;
1429 void *pvBuf = NULL;
1430
1431 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1432 if (pDisk)
1433 {
1434 RTTRACELOGRDR hIoLogRdr = NIL_RTTRACELOGRDR;
1435
1436 rc = RTTraceLogRdrCreateFromFile(&hIoLogRdr, pcszIoLog);
1437 if (RT_SUCCESS(rc))
1438 {
1439 RTTRACELOGRDRPOLLEVT enmEvt = RTTRACELOGRDRPOLLEVT_INVALID;
1440
1441 rc = RTTraceLogRdrEvtPoll(hIoLogRdr, &enmEvt, RT_INDEFINITE_WAIT);
1442 if (RT_SUCCESS(rc))
1443 {
1444 AssertMsg(enmEvt == RTTRACELOGRDRPOLLEVT_HDR_RECVD, ("Expected a header received event but got: %#x\n", enmEvt));
1445
1446 /* Loop through events. */
1447 rc = RTTraceLogRdrEvtPoll(hIoLogRdr, &enmEvt, RT_INDEFINITE_WAIT);
1448 while (RT_SUCCESS(rc))
1449 {
1450 AssertMsg(enmEvt == RTTRACELOGRDRPOLLEVT_TRACE_EVENT_RECVD,
1451 ("Expected a trace event received event but got: %#x\n", enmEvt));
1452
1453 RTTRACELOGRDREVT hEvt = NIL_RTTRACELOGRDREVT;
1454 rc = RTTraceLogRdrQueryLastEvt(hIoLogRdr, &hEvt);
1455 AssertRC(rc);
1456 if (RT_SUCCESS(rc))
1457 {
1458 PCRTTRACELOGEVTDESC pEvtDesc = RTTraceLogRdrEvtGetDesc(hEvt);
1459
1460 if (!RTStrCmp(pEvtDesc->pszId, "Read"))
1461 {
1462 RTTRACELOGEVTVAL aVals[3];
1463 unsigned cVals = 0;
1464 rc = RTTraceLogRdrEvtFillVals(hEvt, 0, &aVals[0], RT_ELEMENTS(aVals), &cVals);
1465 if ( RT_SUCCESS(rc)
1466 && cVals == 3
1467 && aVals[0].pItemDesc->enmType == RTTRACELOGTYPE_BOOL
1468 && aVals[1].pItemDesc->enmType == RTTRACELOGTYPE_UINT64
1469 && aVals[2].pItemDesc->enmType == RTTRACELOGTYPE_SIZE)
1470 {
1471 bool fAsync = aVals[0].u.f;
1472 uint64_t off = aVals[1].u.u64;
1473 size_t cbIo = (size_t)aVals[2].u.sz;
1474
1475 if (cbIo > cbBuf)
1476 {
1477 pvBuf = RTMemRealloc(pvBuf, cbIo);
1478 if (pvBuf)
1479 cbBuf = cbIo;
1480 else
1481 rc = VERR_NO_MEMORY;
1482 }
1483
1484 if ( RT_SUCCESS(rc)
1485 && !fAsync)
1486 rc = VDRead(pDisk->pVD, off, pvBuf, cbIo);
1487 else if (RT_SUCCESS(rc))
1488 rc = VERR_NOT_SUPPORTED;
1489 }
1490 }
1491 else if (!RTStrCmp(pEvtDesc->pszId, "Write"))
1492 {
1493 RTTRACELOGEVTVAL aVals[3];
1494 unsigned cVals = 0;
1495 rc = RTTraceLogRdrEvtFillVals(hEvt, 0, &aVals[0], RT_ELEMENTS(aVals), &cVals);
1496 if ( RT_SUCCESS(rc)
1497 && cVals == 3
1498 && aVals[0].pItemDesc->enmType == RTTRACELOGTYPE_BOOL
1499 && aVals[1].pItemDesc->enmType == RTTRACELOGTYPE_UINT64
1500 && aVals[2].pItemDesc->enmType == RTTRACELOGTYPE_SIZE)
1501 {
1502 bool fAsync = aVals[0].u.f;
1503 uint64_t off = aVals[1].u.u64;
1504 size_t cbIo = (size_t)aVals[2].u.sz;
1505
1506 if (cbIo > cbBuf)
1507 {
1508 pvBuf = RTMemRealloc(pvBuf, cbIo);
1509 if (pvBuf)
1510 cbBuf = cbIo;
1511 else
1512 rc = VERR_NO_MEMORY;
1513 }
1514
1515 if ( RT_SUCCESS(rc)
1516 && !fAsync)
1517 rc = VDWrite(pDisk->pVD, off, pvBuf, cbIo);
1518 else if (RT_SUCCESS(rc))
1519 rc = VERR_NOT_SUPPORTED;
1520 }
1521 }
1522 else if (!RTStrCmp(pEvtDesc->pszId, "Flush"))
1523 {
1524 RTTRACELOGEVTVAL Val;
1525 unsigned cVals = 0;
1526 rc = RTTraceLogRdrEvtFillVals(hEvt, 0, &Val, 1, &cVals);
1527 if ( RT_SUCCESS(rc)
1528 && cVals == 1
1529 && Val.pItemDesc->enmType == RTTRACELOGTYPE_BOOL)
1530 {
1531 bool fAsync = Val.u.f;
1532
1533 if ( RT_SUCCESS(rc)
1534 && !fAsync)
1535 rc = VDFlush(pDisk->pVD);
1536 else if (RT_SUCCESS(rc))
1537 rc = VERR_NOT_SUPPORTED;
1538 }
1539 }
1540 else if (!RTStrCmp(pEvtDesc->pszId, "Discard"))
1541 {}
1542 else
1543 AssertMsgFailed(("Invalid event ID: %s\n", pEvtDesc->pszId));
1544
1545 if (RT_SUCCESS(rc))
1546 {
1547 rc = RTTraceLogRdrEvtPoll(hIoLogRdr, &enmEvt, RT_INDEFINITE_WAIT);
1548 if (RT_SUCCESS(rc))
1549 {
1550 AssertMsg(enmEvt == RTTRACELOGRDRPOLLEVT_TRACE_EVENT_RECVD,
1551 ("Expected a trace event received event but got: %#x\n", enmEvt));
1552
1553 hEvt = NIL_RTTRACELOGRDREVT;
1554 rc = RTTraceLogRdrQueryLastEvt(hIoLogRdr, &hEvt);
1555 if (RT_SUCCESS(rc))
1556 {
1557 pEvtDesc = RTTraceLogRdrEvtGetDesc(hEvt);
1558 AssertMsg(!RTStrCmp(pEvtDesc->pszId, "Complete"),
1559 ("Expected a completion event but got: %s\n", pEvtDesc->pszId));
1560 }
1561 }
1562 }
1563 }
1564
1565 if (RT_FAILURE(rc))
1566 break;
1567
1568 rc = RTTraceLogRdrEvtPoll(hIoLogRdr, &enmEvt, RT_INDEFINITE_WAIT);
1569 }
1570 }
1571
1572 RTTraceLogRdrDestroy(hIoLogRdr);
1573 }
1574 }
1575 else
1576 rc = VERR_NOT_FOUND;
1577
1578 if (pvBuf)
1579 RTMemFree(pvBuf);
1580
1581 return rc;
1582}
1583
1584
1585static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDSCRIPTARG paScriptArgs, void *pvUser)
1586{
1587 int rc = VINF_SUCCESS;
1588 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1589 size_t cbPattern = (size_t)paScriptArgs[0].u64;
1590 const char *pcszSeeder = paScriptArgs[1].psz;
1591 uint64_t uSeed = paScriptArgs[2].u64;
1592
1593 if (pGlob->pIoRnd)
1594 {
1595 RTPrintf("I/O RNG already exists\n");
1596 rc = VERR_INVALID_STATE;
1597 }
1598 else
1599 {
1600 uint64_t uSeedToUse = 0;
1601
1602 if (!RTStrICmp(pcszSeeder, "manual"))
1603 uSeedToUse = uSeed;
1604 else if (!RTStrICmp(pcszSeeder, "time"))
1605 uSeedToUse = RTTimeSystemMilliTS();
1606 else if (!RTStrICmp(pcszSeeder, "system"))
1607 {
1608 RTRAND hRand;
1609 rc = RTRandAdvCreateSystemTruer(&hRand);
1610 if (RT_SUCCESS(rc))
1611 {
1612 RTRandAdvBytes(hRand, &uSeedToUse, sizeof(uSeedToUse));
1613 RTRandAdvDestroy(hRand);
1614 }
1615 }
1616
1617 if (RT_SUCCESS(rc))
1618 rc = VDIoRndCreate(&pGlob->pIoRnd, cbPattern, uSeed);
1619 }
1620
1621 return rc;
1622}
1623
1624static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser)
1625{
1626 RT_NOREF1(paScriptArgs);
1627 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1628
1629 if (pGlob->pIoRnd)
1630 {
1631 VDIoRndDestroy(pGlob->pIoRnd);
1632 pGlob->pIoRnd = NULL;
1633 }
1634 else
1635 RTPrintf("WARNING: No I/O RNG active, faulty script. Continuing\n");
1636
1637 return VINF_SUCCESS;
1638}
1639
1640static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDSCRIPTARG paScriptArgs, void *pvUser)
1641{
1642 int rc = VINF_SUCCESS;
1643 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1644 const char *pcszName = paScriptArgs[0].psz;
1645 size_t cbPattern = (size_t)paScriptArgs[1].u64;
1646 uint64_t u64Pattern = paScriptArgs[2].u64;
1647
1648 PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName);
1649 if (!pPattern)
1650 {
1651 pPattern = tstVDIoPatternCreate(pcszName, RT_ALIGN_Z(cbPattern, sizeof(uint64_t)));
1652 if (pPattern)
1653 {
1654 /* Fill the buffer. */
1655 void *pv = pPattern->pvPattern;
1656
1657 while (pPattern->cbPattern > 0)
1658 {
1659 *((uint64_t*)pv) = u64Pattern;
1660 pPattern->cbPattern -= sizeof(uint64_t);
1661 pv = (uint64_t *)pv + 1;
1662 }
1663 pPattern->cbPattern = cbPattern; /* Set to the desired size. (could be unaligned) */
1664
1665 RTListAppend(&pGlob->ListPatterns, &pPattern->ListNode);
1666 }
1667 else
1668 rc = VERR_NO_MEMORY;
1669 }
1670 else
1671 rc = VERR_ALREADY_EXISTS;
1672
1673 return rc;
1674}
1675
1676static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDSCRIPTARG paScriptArgs, void *pvUser)
1677{
1678 int rc = VINF_SUCCESS;
1679 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1680 const char *pcszName = paScriptArgs[0].psz;
1681 const char *pcszFile = paScriptArgs[1].psz;
1682
1683 PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName);
1684 if (!pPattern)
1685 {
1686 RTFILE hFile;
1687 uint64_t cbPattern = 0;
1688
1689 rc = RTFileOpen(&hFile, pcszFile, RTFILE_O_DENY_NONE | RTFILE_O_OPEN | RTFILE_O_READ);
1690 if (RT_SUCCESS(rc))
1691 {
1692 rc = RTFileQuerySize(hFile, &cbPattern);
1693 if (RT_SUCCESS(rc))
1694 {
1695 pPattern = tstVDIoPatternCreate(pcszName, (size_t)cbPattern);
1696 if (pPattern)
1697 {
1698 rc = RTFileRead(hFile, pPattern->pvPattern, (size_t)cbPattern, NULL);
1699 if (RT_SUCCESS(rc))
1700 RTListAppend(&pGlob->ListPatterns, &pPattern->ListNode);
1701 else
1702 {
1703 RTMemFree(pPattern->pvPattern);
1704 RTStrFree(pPattern->pszName);
1705 RTMemFree(pPattern);
1706 }
1707 }
1708 else
1709 rc = VERR_NO_MEMORY;
1710 }
1711 RTFileClose(hFile);
1712 }
1713 }
1714 else
1715 rc = VERR_ALREADY_EXISTS;
1716
1717 return rc;
1718}
1719
1720static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser)
1721{
1722 int rc = VINF_SUCCESS;
1723 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1724 const char *pcszName = paScriptArgs[0].psz;
1725
1726 PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName);
1727 if (pPattern)
1728 {
1729 RTListNodeRemove(&pPattern->ListNode);
1730 RTMemFree(pPattern->pvPattern);
1731 RTStrFree(pPattern->pszName);
1732 RTMemFree(pPattern);
1733 }
1734 else
1735 rc = VERR_NOT_FOUND;
1736
1737 return rc;
1738}
1739
1740static DECLCALLBACK(int) vdScriptHandlerSleep(PVDSCRIPTARG paScriptArgs, void *pvUser)
1741{
1742 RT_NOREF1(pvUser);
1743 uint64_t cMillies = paScriptArgs[0].u64;
1744
1745 int rc = RTThreadSleep(cMillies);
1746 return rc;
1747}
1748
1749static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDSCRIPTARG paScriptArgs, void *pvUser)
1750{
1751 int rc = VINF_SUCCESS;
1752 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1753 const char *pcszFile = paScriptArgs[0].psz;
1754 const char *pcszPathToDump = paScriptArgs[1].psz;
1755
1756 /* Check for the file. */
1757 bool fFound = false;
1758 PVDFILE pIt;
1759 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1760 {
1761 if (!RTStrCmp(pIt->pszName, pcszFile))
1762 {
1763 fFound = true;
1764 break;
1765 }
1766 }
1767
1768 if (fFound)
1769 {
1770 RTPrintf("Dumping memory file %s to %s, this might take some time\n", pcszFile, pcszPathToDump);
1771 rc = VDIoBackendDumpToFile(pIt->pIoStorage, pcszPathToDump);
1772 rc = VERR_NOT_IMPLEMENTED;
1773 }
1774 else
1775 rc = VERR_FILE_NOT_FOUND;
1776
1777 return rc;
1778}
1779
1780static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDSCRIPTARG paScriptArgs, void *pvUser)
1781{
1782 int rc = VINF_SUCCESS;
1783 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1784 const char *pcszDisk = NULL;
1785 PVDDISK pDisk = NULL;
1786 bool fVerify = false;
1787
1788 pcszDisk = paScriptArgs[0].psz;
1789 fVerify = paScriptArgs[1].f;
1790
1791 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1792 if (pDisk)
1793 rc = VERR_ALREADY_EXISTS;
1794 else
1795 {
1796 pDisk = (PVDDISK)RTMemAllocZ(sizeof(VDDISK));
1797 if (pDisk)
1798 {
1799 pDisk->pTestGlob = pGlob;
1800 pDisk->pszName = RTStrDup(pcszDisk);
1801 if (pDisk->pszName)
1802 {
1803 rc = VINF_SUCCESS;
1804
1805 if (fVerify)
1806 {
1807 rc = VDMemDiskCreate(&pDisk->pMemDiskVerify, 0 /* Growing */);
1808 if (RT_SUCCESS(rc))
1809 {
1810 rc = RTCritSectInit(&pDisk->CritSectVerify);
1811 if (RT_FAILURE(rc))
1812 VDMemDiskDestroy(pDisk->pMemDiskVerify);
1813 }
1814 }
1815
1816 if (RT_SUCCESS(rc))
1817 {
1818 rc = VDCreate(pGlob->pInterfacesDisk, VDTYPE_HDD, &pDisk->pVD);
1819
1820 if (RT_SUCCESS(rc))
1821 RTListAppend(&pGlob->ListDisks, &pDisk->ListNode);
1822 else
1823 {
1824 if (fVerify)
1825 {
1826 RTCritSectDelete(&pDisk->CritSectVerify);
1827 VDMemDiskDestroy(pDisk->pMemDiskVerify);
1828 }
1829 RTStrFree(pDisk->pszName);
1830 }
1831 }
1832 }
1833 else
1834 rc = VERR_NO_MEMORY;
1835
1836 if (RT_FAILURE(rc))
1837 RTMemFree(pDisk);
1838 }
1839 else
1840 rc = VERR_NO_MEMORY;
1841 }
1842 return rc;
1843}
1844
1845static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDSCRIPTARG paScriptArgs, void *pvUser)
1846{
1847 int rc = VINF_SUCCESS;
1848 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1849 const char *pcszDisk = NULL;
1850 PVDDISK pDisk = NULL;
1851
1852 pcszDisk = paScriptArgs[0].psz;
1853
1854 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1855 if (pDisk)
1856 {
1857 RTListNodeRemove(&pDisk->ListNode);
1858 VDDestroy(pDisk->pVD);
1859 if (pDisk->pMemDiskVerify)
1860 {
1861 VDMemDiskDestroy(pDisk->pMemDiskVerify);
1862 RTCritSectDelete(&pDisk->CritSectVerify);
1863 }
1864 RTStrFree(pDisk->pszName);
1865 RTMemFree(pDisk);
1866 }
1867 else
1868 rc = VERR_NOT_FOUND;
1869
1870 return rc;
1871}
1872
1873static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDSCRIPTARG paScriptArgs, void *pvUser)
1874{
1875 int rc = VINF_SUCCESS;
1876 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1877 const char *pcszDisk1 = NULL;
1878 PVDDISK pDisk1 = NULL;
1879 const char *pcszDisk2 = NULL;
1880 PVDDISK pDisk2 = NULL;
1881
1882 pcszDisk1 = paScriptArgs[0].psz;
1883 pcszDisk2 = paScriptArgs[1].psz;
1884
1885 pDisk1 = tstVDIoGetDiskByName(pGlob, pcszDisk1);
1886 pDisk2 = tstVDIoGetDiskByName(pGlob, pcszDisk2);
1887
1888 if (pDisk1 && pDisk2)
1889 {
1890 uint8_t *pbBuf1 = (uint8_t *)RTMemAllocZ(16 * _1M);
1891 uint8_t *pbBuf2 = (uint8_t *)RTMemAllocZ(16 * _1M);
1892 if (pbBuf1 && pbBuf2)
1893 {
1894 uint64_t cbDisk1, cbDisk2;
1895 uint64_t uOffCur = 0;
1896
1897 cbDisk1 = VDGetSize(pDisk1->pVD, VD_LAST_IMAGE);
1898 cbDisk2 = VDGetSize(pDisk2->pVD, VD_LAST_IMAGE);
1899
1900 RTTestSub(pGlob->hTest, "Comparing two disks for equal content");
1901 if (cbDisk1 != cbDisk2)
1902 RTTestFailed(pGlob->hTest, "Disks differ in size %llu vs %llu\n", cbDisk1, cbDisk2);
1903 else
1904 {
1905 while (uOffCur < cbDisk1)
1906 {
1907 size_t cbRead = RT_MIN(cbDisk1, 16 * _1M);
1908
1909 rc = VDRead(pDisk1->pVD, uOffCur, pbBuf1, cbRead);
1910 if (RT_SUCCESS(rc))
1911 rc = VDRead(pDisk2->pVD, uOffCur, pbBuf2, cbRead);
1912
1913 if (RT_SUCCESS(rc))
1914 {
1915 if (memcmp(pbBuf1, pbBuf2, cbRead))
1916 {
1917 RTTestFailed(pGlob->hTest, "Disks differ at offset %llu\n", uOffCur);
1918 rc = VERR_DEV_IO_ERROR;
1919 break;
1920 }
1921 }
1922 else
1923 {
1924 RTTestFailed(pGlob->hTest, "Reading one disk at offset %llu failed\n", uOffCur);
1925 break;
1926 }
1927
1928 uOffCur += cbRead;
1929 cbDisk1 -= cbRead;
1930 }
1931 }
1932 RTMemFree(pbBuf1);
1933 RTMemFree(pbBuf2);
1934 }
1935 else
1936 {
1937 if (pbBuf1)
1938 RTMemFree(pbBuf1);
1939 if (pbBuf2)
1940 RTMemFree(pbBuf2);
1941 rc = VERR_NO_MEMORY;
1942 }
1943 }
1944 else
1945 rc = VERR_NOT_FOUND;
1946
1947 return rc;
1948}
1949
1950static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDSCRIPTARG paScriptArgs, void *pvUser)
1951{
1952 int rc = VINF_SUCCESS;
1953 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1954 const char *pcszDisk = NULL;
1955 PVDDISK pDisk = NULL;
1956
1957 pcszDisk = paScriptArgs[0].psz;
1958
1959 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1960
1961 if (pDisk)
1962 VDDumpImages(pDisk->pVD);
1963 else
1964 rc = VERR_NOT_FOUND;
1965
1966 return rc;
1967}
1968
1969static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDSCRIPTARG paScriptArgs, void *pvUser)
1970{
1971 RT_NOREF1(pvUser);
1972 RTPrintf("%s\n", paScriptArgs[0].psz);
1973 return VINF_SUCCESS;
1974}
1975
1976static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser)
1977{
1978 int rc = VINF_SUCCESS;
1979 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1980 const char *pcszFile = paScriptArgs[0].psz;
1981
1982 /* Check for the file. */
1983 bool fFound = false;
1984 PVDFILE pIt;
1985 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1986 {
1987 if (!RTStrCmp(pIt->pszName, pcszFile))
1988 {
1989 fFound = true;
1990 break;
1991 }
1992 }
1993
1994 if (fFound)
1995 {
1996 RTPrintf("Statistics %s: \n"
1997 " sync reads=%u writes=%u flushes=%u\n"
1998 " async reads=%u writes=%u flushes=%u\n",
1999 pcszFile,
2000 pIt->cReads, pIt->cWrites, pIt->cFlushes,
2001 pIt->cAsyncReads, pIt->cAsyncWrites, pIt->cAsyncFlushes);
2002 }
2003 else
2004 rc = VERR_FILE_NOT_FOUND;
2005
2006 return rc;
2007}
2008
2009static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser)
2010{
2011 int rc = VINF_SUCCESS;
2012 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2013 const char *pcszFile = paScriptArgs[0].psz;
2014
2015 /* Check for the file. */
2016 bool fFound = false;
2017 PVDFILE pIt;
2018 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
2019 {
2020 if (!RTStrCmp(pIt->pszName, pcszFile))
2021 {
2022 fFound = true;
2023 break;
2024 }
2025 }
2026
2027 if (fFound)
2028 {
2029 pIt->cReads = 0;
2030 pIt->cWrites = 0;
2031 pIt->cFlushes = 0;
2032
2033 pIt->cAsyncReads = 0;
2034 pIt->cAsyncWrites = 0;
2035 pIt->cAsyncFlushes = 0;
2036 }
2037 else
2038 rc = VERR_FILE_NOT_FOUND;
2039
2040 return rc;
2041}
2042
2043static DECLCALLBACK(int) vdScriptHandlerResize(PVDSCRIPTARG paScriptArgs, void *pvUser)
2044{
2045 int rc = VINF_SUCCESS;
2046 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2047 const char *pcszDisk = paScriptArgs[0].psz;
2048 uint64_t cbDiskNew = paScriptArgs[1].u64;
2049 PVDDISK pDisk = NULL;
2050
2051 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
2052 if (pDisk)
2053 {
2054 rc = VDResize(pDisk->pVD, cbDiskNew, &pDisk->PhysGeom, &pDisk->LogicalGeom, NULL);
2055 }
2056 else
2057 rc = VERR_NOT_FOUND;
2058
2059 return rc;
2060}
2061
2062static DECLCALLBACK(int) vdScriptHandlerSetFileBackend(PVDSCRIPTARG paScriptArgs, void *pvUser)
2063{
2064 int rc = VINF_SUCCESS;
2065 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2066 const char *pcszBackend = paScriptArgs[0].psz;
2067
2068 RTStrFree(pGlob->pszIoBackend);
2069 pGlob->pszIoBackend = RTStrDup(pcszBackend);
2070 if (!pGlob->pszIoBackend)
2071 rc = VERR_NO_MEMORY;
2072
2073 return rc;
2074}
2075
2076static DECLCALLBACK(int) vdScriptHandlerLoadPlugin(PVDSCRIPTARG paScriptArgs, void *pvUser)
2077{
2078 RT_NOREF(pvUser);
2079 const char *pcszPlugin = paScriptArgs[0].psz;
2080
2081 return VDPluginLoadFromFilename(pcszPlugin);
2082}
2083
2084static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation,
2085 uint32_t fOpen,
2086 PFNVDCOMPLETED pfnCompleted,
2087 void **ppStorage)
2088{
2089 int rc = VINF_SUCCESS;
2090 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2091 bool fFound = false;
2092
2093 /*
2094 * Some backends use ./ for paths, strip it.
2095 * @todo: Implement proper directory support for the
2096 * memory filesystem.
2097 */
2098 if ( strlen(pszLocation) >= 2
2099 && *pszLocation == '.'
2100 && ( pszLocation[1] == '/'
2101 || pszLocation[1] == '\\'))
2102 pszLocation += 2;
2103
2104 /* Check if the file exists. */
2105 PVDFILE pIt;
2106 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
2107 {
2108 if (!RTStrCmp(pIt->pszName, pszLocation))
2109 {
2110 fFound = true;
2111 break;
2112 }
2113 }
2114
2115 if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE)
2116 {
2117 /* If the file exists delete the memory disk. */
2118 if (fFound)
2119 rc = VDIoBackendStorageSetSize(pIt->pIoStorage, 0);
2120 else
2121 {
2122 /* Create completey new. */
2123 pIt = (PVDFILE)RTMemAllocZ(sizeof(VDFILE));
2124 if (pIt)
2125 {
2126 pIt->pszName = RTStrDup(pszLocation);
2127
2128 if (pIt->pszName)
2129 {
2130 rc = VDIoBackendStorageCreate(pGlob->pIoBackend, pGlob->pszIoBackend,
2131 pszLocation, pfnCompleted, &pIt->pIoStorage);
2132 }
2133 else
2134 rc = VERR_NO_MEMORY;
2135
2136 if (RT_FAILURE(rc))
2137 {
2138 if (pIt->pszName)
2139 RTStrFree(pIt->pszName);
2140 RTMemFree(pIt);
2141 }
2142 else
2143 RTListAppend(&pGlob->ListFiles, &pIt->Node);
2144 }
2145 else
2146 rc = VERR_NO_MEMORY;
2147 }
2148 }
2149 else if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN)
2150 {
2151 if (!fFound)
2152 rc = VERR_FILE_NOT_FOUND;
2153 }
2154 else
2155 rc = VERR_INVALID_PARAMETER;
2156
2157 if (RT_SUCCESS(rc))
2158 {
2159 AssertPtr(pIt);
2160 PVDSTORAGE pStorage = (PVDSTORAGE)RTMemAllocZ(sizeof(VDSTORAGE));
2161 if (pStorage)
2162 {
2163 pStorage->pFile = pIt;
2164 pStorage->pfnComplete = pfnCompleted;
2165 *ppStorage = pStorage;
2166 }
2167 else
2168 rc = VERR_NO_MEMORY;
2169 }
2170
2171 return rc;
2172}
2173
2174static DECLCALLBACK(int) tstVDIoFileClose(void *pvUser, void *pStorage)
2175{
2176 RT_NOREF1(pvUser);
2177 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2178
2179 RTMemFree(pIoStorage);
2180 return VINF_SUCCESS;
2181}
2182
2183static DECLCALLBACK(int) tstVDIoFileDelete(void *pvUser, const char *pcszFilename)
2184{
2185 int rc = VINF_SUCCESS;
2186 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2187 bool fFound = false;
2188
2189 /*
2190 * Some backends use ./ for paths, strip it.
2191 * @todo: Implement proper directory support for the
2192 * memory filesystem.
2193 */
2194 if ( strlen(pcszFilename) >= 2
2195 && *pcszFilename == '.'
2196 && pcszFilename[1] == '/')
2197 pcszFilename += 2;
2198
2199 /* Check if the file exists. */
2200 PVDFILE pIt;
2201 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
2202 {
2203 if (!RTStrCmp(pIt->pszName, pcszFilename))
2204 {
2205 fFound = true;
2206 break;
2207 }
2208 }
2209
2210 if (fFound)
2211 {
2212 RTListNodeRemove(&pIt->Node);
2213 VDIoBackendStorageDestroy(pIt->pIoStorage);
2214 RTStrFree(pIt->pszName);
2215 RTMemFree(pIt);
2216 }
2217 else
2218 rc = VERR_FILE_NOT_FOUND;
2219
2220 return rc;
2221}
2222
2223static DECLCALLBACK(int) tstVDIoFileMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
2224{
2225 RT_NOREF1(fMove);
2226 int rc = VINF_SUCCESS;
2227 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2228 bool fFound = false;
2229
2230 /* Check if the file exists. */
2231 PVDFILE pIt;
2232 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
2233 {
2234 if (!RTStrCmp(pIt->pszName, pcszSrc))
2235 {
2236 fFound = true;
2237 break;
2238 }
2239 }
2240
2241 if (fFound)
2242 {
2243 char *pszNew = RTStrDup(pcszDst);
2244 if (pszNew)
2245 {
2246 RTStrFree(pIt->pszName);
2247 pIt->pszName = pszNew;
2248 }
2249 else
2250 rc = VERR_NO_MEMORY;
2251 }
2252 else
2253 rc = VERR_FILE_NOT_FOUND;
2254
2255 return rc;
2256}
2257
2258static DECLCALLBACK(int) tstVDIoFileGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
2259{
2260 RT_NOREF2(pvUser, pcszFilename);
2261 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
2262
2263 *pcbFreeSpace = ~0ULL; /** @todo Implement */
2264 return VINF_SUCCESS;
2265}
2266
2267static DECLCALLBACK(int) tstVDIoFileGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
2268{
2269 RT_NOREF2(pvUser, pcszFilename);
2270 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
2271
2272 /** @todo Implement */
2273 return VINF_SUCCESS;
2274}
2275
2276static DECLCALLBACK(int) tstVDIoFileGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
2277{
2278 RT_NOREF1(pvUser);
2279 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2280
2281 return VDIoBackendStorageGetSize(pIoStorage->pFile->pIoStorage, pcbSize);
2282}
2283
2284static DECLCALLBACK(int) tstVDIoFileSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
2285{
2286 RT_NOREF1(pvUser);
2287 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2288
2289 return VDIoBackendStorageSetSize(pIoStorage->pFile->pIoStorage, cbSize);
2290}
2291
2292static DECLCALLBACK(int) tstVDIoFileSetAllocationSize(void *pvUser, void *pStorage, uint64_t cbSize, uint32_t fFlags)
2293{
2294 RT_NOREF4(pvUser, pStorage, cbSize, fFlags);
2295 return VERR_NOT_SUPPORTED;
2296}
2297
2298static DECLCALLBACK(int) tstVDIoFileWriteSync(void *pvUser, void *pStorage, uint64_t uOffset,
2299 const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten)
2300{
2301 RT_NOREF1(pvUser);
2302 int rc = VINF_SUCCESS;
2303 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2304
2305 RTSGBUF SgBuf;
2306 RTSGSEG Seg;
2307
2308 Seg.pvSeg = (void *)pvBuffer;
2309 Seg.cbSeg = cbBuffer;
2310 RTSgBufInit(&SgBuf, &Seg, 1);
2311 rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_WRITE, uOffset,
2312 cbBuffer, &SgBuf, NULL, true /* fSync */);
2313 if (RT_SUCCESS(rc))
2314 {
2315 pIoStorage->pFile->cWrites++;
2316 if (pcbWritten)
2317 *pcbWritten = cbBuffer;
2318 }
2319
2320 return rc;
2321}
2322
2323static DECLCALLBACK(int) tstVDIoFileReadSync(void *pvUser, void *pStorage, uint64_t uOffset,
2324 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
2325{
2326 RT_NOREF1(pvUser);
2327 int rc = VINF_SUCCESS;
2328 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2329
2330 RTSGBUF SgBuf;
2331 RTSGSEG Seg;
2332
2333 Seg.pvSeg = pvBuffer;
2334 Seg.cbSeg = cbBuffer;
2335 RTSgBufInit(&SgBuf, &Seg, 1);
2336 rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_READ, uOffset,
2337 cbBuffer, &SgBuf, NULL, true /* fSync */);
2338 if (RT_SUCCESS(rc))
2339 {
2340 pIoStorage->pFile->cReads++;
2341 if (pcbRead)
2342 *pcbRead = cbBuffer;
2343 }
2344
2345 return rc;
2346}
2347
2348static DECLCALLBACK(int) tstVDIoFileFlushSync(void *pvUser, void *pStorage)
2349{
2350 RT_NOREF1(pvUser);
2351 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2352 int rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_FLUSH, 0,
2353 0, NULL, NULL, true /* fSync */);
2354 pIoStorage->pFile->cFlushes++;
2355 return rc;
2356}
2357
2358static DECLCALLBACK(int) tstVDIoFileReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
2359 PCRTSGSEG paSegments, size_t cSegments,
2360 size_t cbRead, void *pvCompletion,
2361 void **ppTask)
2362{
2363 RT_NOREF2(pvUser, ppTask);
2364 int rc = VINF_SUCCESS;
2365 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2366 RTSGBUF SgBuf;
2367
2368 RTSgBufInit(&SgBuf, paSegments, cSegments);
2369 rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_READ, uOffset,
2370 cbRead, &SgBuf, pvCompletion, false /* fSync */);
2371 if (RT_SUCCESS(rc))
2372 {
2373 pIoStorage->pFile->cAsyncReads++;
2374 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2375 }
2376
2377 return rc;
2378}
2379
2380static DECLCALLBACK(int) tstVDIoFileWriteAsync(void *pvUser, void *pStorage, uint64_t uOffset,
2381 PCRTSGSEG paSegments, size_t cSegments,
2382 size_t cbWrite, void *pvCompletion,
2383 void **ppTask)
2384{
2385 RT_NOREF2(pvUser, ppTask);
2386 int rc = VINF_SUCCESS;
2387 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2388 RTSGBUF SgBuf;
2389
2390 RTSgBufInit(&SgBuf, paSegments, cSegments);
2391 rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_WRITE, uOffset,
2392 cbWrite, &SgBuf, pvCompletion, false /* fSync */);
2393 if (RT_SUCCESS(rc))
2394 {
2395 pIoStorage->pFile->cAsyncWrites++;
2396 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2397 }
2398
2399 return rc;
2400}
2401
2402static DECLCALLBACK(int) tstVDIoFileFlushAsync(void *pvUser, void *pStorage, void *pvCompletion,
2403 void **ppTask)
2404{
2405 RT_NOREF2(pvUser, ppTask);
2406 int rc = VINF_SUCCESS;
2407 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2408
2409 rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_FLUSH, 0,
2410 0, NULL, pvCompletion, false /* fSync */);
2411 if (RT_SUCCESS(rc))
2412 {
2413 pIoStorage->pFile->cAsyncFlushes++;
2414 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2415 }
2416
2417 return rc;
2418}
2419
2420static int tstVDIoTestInit(PVDIOTEST pIoTest, PVDTESTGLOB pGlob, bool fRandomAcc, uint32_t cSegsMax,
2421 uint64_t cbIo, size_t cbBlkSize, uint64_t offStart, uint64_t offEnd,
2422 unsigned uWriteChance, PVDPATTERN pPattern)
2423{
2424 int rc = VINF_SUCCESS;
2425
2426 RT_ZERO(*pIoTest);
2427 pIoTest->fRandomAccess = fRandomAcc;
2428 pIoTest->cbIo = cbIo;
2429 pIoTest->cbBlkIo = cbBlkSize;
2430 pIoTest->offStart = offStart;
2431 pIoTest->offEnd = offEnd;
2432 pIoTest->uWriteChance = uWriteChance;
2433 pIoTest->cSegsMax = cSegsMax;
2434 pIoTest->pIoRnd = pGlob->pIoRnd;
2435 pIoTest->pPattern = pPattern;
2436
2437 if (fRandomAcc)
2438 {
2439 uint64_t cbRange = pIoTest->offEnd < pIoTest->offStart
2440 ? pIoTest->offStart - pIoTest->offEnd
2441 : pIoTest->offEnd - pIoTest->offStart;
2442
2443 pIoTest->u.Rnd.cBlocks = (uint32_t)(cbRange / cbBlkSize + (cbRange % cbBlkSize ? 1 : 0));
2444 pIoTest->u.Rnd.cBlocksLeft = pIoTest->u.Rnd.cBlocks;
2445 pIoTest->u.Rnd.pbMapAccessed = (uint8_t *)RTMemAllocZ(pIoTest->u.Rnd.cBlocks / 8
2446 + ((pIoTest->u.Rnd.cBlocks % 8)
2447 ? 1
2448 : 0));
2449 if (!pIoTest->u.Rnd.pbMapAccessed)
2450 rc = VERR_NO_MEMORY;
2451 }
2452 else
2453 pIoTest->u.offNext = pIoTest->offEnd < pIoTest->offStart ? pIoTest->offStart - cbBlkSize : offStart;
2454
2455 return rc;
2456}
2457
2458static void tstVDIoTestDestroy(PVDIOTEST pIoTest)
2459{
2460 if (pIoTest->fRandomAccess)
2461 RTMemFree(pIoTest->u.Rnd.pbMapAccessed);
2462}
2463
2464static bool tstVDIoTestRunning(PVDIOTEST pIoTest)
2465{
2466 return pIoTest->cbIo > 0;
2467}
2468
2469static bool tstVDIoTestReqOutstanding(PTSTVDIOREQ pIoReq)
2470{
2471 return pIoReq->fOutstanding;
2472}
2473
2474static uint32_t tstVDIoTestReqInitSegments(PVDIOTEST pIoTest, PRTSGSEG paSegs, uint32_t cSegs, void *pvBuf, size_t cbBuf)
2475{
2476 uint8_t *pbBuf = (uint8_t *)pvBuf;
2477 size_t cSectorsLeft = cbBuf / 512;
2478 uint32_t iSeg = 0;
2479
2480 /* Init all but the last segment which needs to take the rest. */
2481 while ( iSeg < cSegs - 1
2482 && cSectorsLeft)
2483 {
2484 uint32_t cThisSectors = VDIoRndGetU32Ex(pIoTest->pIoRnd, 1, (uint32_t)cSectorsLeft / 2);
2485 size_t cbThisBuf = cThisSectors * 512;
2486
2487 paSegs[iSeg].pvSeg = pbBuf;
2488 paSegs[iSeg].cbSeg = cbThisBuf;
2489 pbBuf += cbThisBuf;
2490 cSectorsLeft -= cThisSectors;
2491 iSeg++;
2492 }
2493
2494 if (cSectorsLeft)
2495 {
2496 paSegs[iSeg].pvSeg = pbBuf;
2497 paSegs[iSeg].cbSeg = cSectorsLeft * 512;
2498 iSeg++;
2499 }
2500
2501 return iSeg;
2502}
2503
2504/**
2505 * Returns true with the given chance in percent.
2506 *
2507 * @returns true or false
2508 * @param iPercentage The percentage of the chance to return true.
2509 */
2510static bool tstVDIoTestIsTrue(PVDIOTEST pIoTest, int iPercentage)
2511{
2512 int uRnd = VDIoRndGetU32Ex(pIoTest->pIoRnd, 0, 100);
2513
2514 return (uRnd < iPercentage); /* This should be enough for our purpose */
2515}
2516
2517static int tstVDIoTestReqInit(PVDIOTEST pIoTest, PTSTVDIOREQ pIoReq, void *pvUser)
2518{
2519 int rc = VINF_SUCCESS;
2520
2521 if (pIoTest->cbIo)
2522 {
2523 /* Read or Write? */
2524 pIoReq->enmTxDir = tstVDIoTestIsTrue(pIoTest, pIoTest->uWriteChance) ? TSTVDIOREQTXDIR_WRITE : TSTVDIOREQTXDIR_READ;
2525 pIoReq->cbReq = RT_MIN(pIoTest->cbBlkIo, pIoTest->cbIo);
2526 pIoTest->cbIo -= pIoReq->cbReq;
2527
2528 void *pvBuf = NULL;
2529
2530 if (pIoReq->enmTxDir == TSTVDIOREQTXDIR_WRITE)
2531 {
2532 if (pIoTest->pPattern)
2533 rc = tstVDIoPatternGetBuffer(pIoTest->pPattern, &pvBuf, pIoReq->cbReq);
2534 else
2535 rc = VDIoRndGetBuffer(pIoTest->pIoRnd, &pvBuf, pIoReq->cbReq);
2536 AssertRC(rc);
2537 }
2538 else
2539 {
2540 /* Read */
2541 pvBuf = pIoReq->pvBufRead;
2542 }
2543
2544 if (RT_SUCCESS(rc))
2545 {
2546 pIoReq->pvBuf = pvBuf;
2547 uint32_t cSegsMax = VDIoRndGetU32Ex(pIoTest->pIoRnd, 1, RT_MIN(pIoTest->cSegsMax, RT_ELEMENTS(pIoReq->aSegs)));
2548 pIoReq->cSegs = tstVDIoTestReqInitSegments(pIoTest, &pIoReq->aSegs[0], cSegsMax, pvBuf, pIoReq->cbReq);
2549 RTSgBufInit(&pIoReq->SgBuf, &pIoReq->aSegs[0], pIoReq->cSegs);
2550
2551 if (pIoTest->fRandomAccess)
2552 {
2553 int idx = -1;
2554
2555 idx = ASMBitFirstClear(pIoTest->u.Rnd.pbMapAccessed, pIoTest->u.Rnd.cBlocks);
2556
2557 /* In case this is the last request we don't need to search further. */
2558 if (pIoTest->u.Rnd.cBlocksLeft > 1)
2559 {
2560 int idxIo;
2561 idxIo = VDIoRndGetU32Ex(pIoTest->pIoRnd, idx, pIoTest->u.Rnd.cBlocks - 1);
2562
2563 /*
2564 * If the bit is marked free use it, otherwise search for the next free bit
2565 * and if that doesn't work use the first free bit.
2566 */
2567 if (ASMBitTest(pIoTest->u.Rnd.pbMapAccessed, idxIo))
2568 {
2569 idxIo = ASMBitNextClear(pIoTest->u.Rnd.pbMapAccessed, pIoTest->u.Rnd.cBlocks, idxIo);
2570 if (idxIo != -1)
2571 idx = idxIo;
2572 }
2573 else
2574 idx = idxIo;
2575 }
2576
2577 Assert(idx != -1);
2578 pIoReq->off = (uint64_t)idx * pIoTest->cbBlkIo;
2579 pIoTest->u.Rnd.cBlocksLeft--;
2580 if (!pIoTest->u.Rnd.cBlocksLeft)
2581 {
2582 /* New round, clear everything. */
2583 ASMBitClearRange(pIoTest->u.Rnd.pbMapAccessed, 0, pIoTest->u.Rnd.cBlocks);
2584 pIoTest->u.Rnd.cBlocksLeft = pIoTest->u.Rnd.cBlocks;
2585 }
2586 else
2587 ASMBitSet(pIoTest->u.Rnd.pbMapAccessed, idx);
2588 }
2589 else
2590 {
2591 pIoReq->off = pIoTest->u.offNext;
2592 if (pIoTest->offEnd < pIoTest->offStart)
2593 {
2594 pIoTest->u.offNext = pIoTest->u.offNext == 0
2595 ? pIoTest->offEnd - pIoTest->cbBlkIo
2596 : RT_MAX(pIoTest->offEnd, pIoTest->u.offNext - pIoTest->cbBlkIo);
2597 }
2598 else
2599 {
2600 pIoTest->u.offNext = pIoTest->u.offNext + pIoTest->cbBlkIo >= pIoTest->offEnd
2601 ? 0
2602 : RT_MIN(pIoTest->offEnd, pIoTest->u.offNext + pIoTest->cbBlkIo);
2603 }
2604 }
2605 pIoReq->pvUser = pvUser;
2606 pIoReq->fOutstanding = true;
2607 }
2608 }
2609 else
2610 rc = VERR_ACCESS_DENIED;
2611
2612 return rc;
2613}
2614
2615static DECLCALLBACK(void) tstVDIoTestReqComplete(void *pvUser1, void *pvUser2, int rcReq)
2616{
2617 RT_NOREF1(rcReq);
2618 PTSTVDIOREQ pIoReq = (PTSTVDIOREQ)pvUser1;
2619 RTSEMEVENT hEventSem = (RTSEMEVENT)pvUser2;
2620 PVDDISK pDisk = (PVDDISK)pIoReq->pvUser;
2621
2622 LogFlow(("Request %d completed\n", pIoReq->idx));
2623
2624 if (pDisk->pMemDiskVerify)
2625 {
2626 switch (pIoReq->enmTxDir)
2627 {
2628 case TSTVDIOREQTXDIR_READ:
2629 {
2630 RTCritSectEnter(&pDisk->CritSectVerify);
2631
2632 RTSGBUF SgBufCmp;
2633 RTSGSEG SegCmp;
2634 SegCmp.pvSeg = pIoReq->pvBuf;
2635 SegCmp.cbSeg = pIoReq->cbReq;
2636 RTSgBufInit(&SgBufCmp, &SegCmp, 1);
2637
2638 if (VDMemDiskCmp(pDisk->pMemDiskVerify, pIoReq->off, pIoReq->cbReq,
2639 &SgBufCmp))
2640 RTTestFailed(pDisk->pTestGlob->hTest, "Corrupted disk at offset %llu!\n", pIoReq->off);
2641 RTCritSectLeave(&pDisk->CritSectVerify);
2642 break;
2643 }
2644 case TSTVDIOREQTXDIR_WRITE:
2645 {
2646 RTCritSectEnter(&pDisk->CritSectVerify);
2647
2648 RTSGBUF SgBuf;
2649 RTSGSEG Seg;
2650 Seg.pvSeg = pIoReq->pvBuf;
2651 Seg.cbSeg = pIoReq->cbReq;
2652 RTSgBufInit(&SgBuf, &Seg, 1);
2653
2654 int rc = VDMemDiskWrite(pDisk->pMemDiskVerify, pIoReq->off, pIoReq->cbReq,
2655 &SgBuf);
2656 AssertRC(rc);
2657 RTCritSectLeave(&pDisk->CritSectVerify);
2658 break;
2659 }
2660 case TSTVDIOREQTXDIR_FLUSH:
2661 case TSTVDIOREQTXDIR_DISCARD:
2662 break;
2663 }
2664 }
2665
2666 ASMAtomicXchgBool(&pIoReq->fOutstanding, false);
2667 RTSemEventSignal(hEventSem);
2668 return;
2669}
2670
2671/**
2672 * Returns the disk handle by name or NULL if not found
2673 *
2674 * @returns Disk handle or NULL if the disk could not be found.
2675 *
2676 * @param pGlob Global test state.
2677 * @param pcszDisk Name of the disk to get.
2678 */
2679static PVDDISK tstVDIoGetDiskByName(PVDTESTGLOB pGlob, const char *pcszDisk)
2680{
2681 bool fFound = false;
2682
2683 LogFlowFunc(("pGlob=%#p pcszDisk=%s\n", pGlob, pcszDisk));
2684
2685 PVDDISK pIt;
2686 RTListForEach(&pGlob->ListDisks, pIt, VDDISK, ListNode)
2687 {
2688 if (!RTStrCmp(pIt->pszName, pcszDisk))
2689 {
2690 fFound = true;
2691 break;
2692 }
2693 }
2694
2695 LogFlowFunc(("return %#p\n", fFound ? pIt : NULL));
2696 return fFound ? pIt : NULL;
2697}
2698
2699/**
2700 * Returns the I/O pattern handle by name of NULL if not found.
2701 *
2702 * @returns I/O pattern handle or NULL if the pattern could not be found.
2703 *
2704 * @param pGlob Global test state.
2705 * @param pcszName Name of the pattern.
2706 */
2707static PVDPATTERN tstVDIoGetPatternByName(PVDTESTGLOB pGlob, const char *pcszName)
2708{
2709 bool fFound = false;
2710
2711 LogFlowFunc(("pGlob=%#p pcszName=%s\n", pGlob, pcszName));
2712
2713 PVDPATTERN pIt;
2714 RTListForEach(&pGlob->ListPatterns, pIt, VDPATTERN, ListNode)
2715 {
2716 if (!RTStrCmp(pIt->pszName, pcszName))
2717 {
2718 fFound = true;
2719 break;
2720 }
2721 }
2722
2723 LogFlowFunc(("return %#p\n", fFound ? pIt : NULL));
2724 return fFound ? pIt : NULL;
2725}
2726
2727/**
2728 * Creates a new pattern with the given name and an
2729 * allocated pattern buffer.
2730 *
2731 * @returns Pointer to a new pattern buffer or NULL on failure.
2732 * @param pcszName Name of the pattern.
2733 * @param cbPattern Size of the pattern buffer.
2734 */
2735static PVDPATTERN tstVDIoPatternCreate(const char *pcszName, size_t cbPattern)
2736{
2737 PVDPATTERN pPattern = (PVDPATTERN)RTMemAllocZ(sizeof(VDPATTERN));
2738 char *pszName = RTStrDup(pcszName);
2739 void *pvPattern = RTMemAllocZ(cbPattern);
2740
2741 if (pPattern && pszName && pvPattern)
2742 {
2743 pPattern->pszName = pszName;
2744 pPattern->pvPattern = pvPattern;
2745 pPattern->cbPattern = cbPattern;
2746 }
2747 else
2748 {
2749 if (pPattern)
2750 RTMemFree(pPattern);
2751 if (pszName)
2752 RTStrFree(pszName);
2753 if (pvPattern)
2754 RTMemFree(pvPattern);
2755
2756 pPattern = NULL;
2757 pszName = NULL;
2758 pvPattern = NULL;
2759 }
2760
2761 return pPattern;
2762}
2763
2764static int tstVDIoPatternGetBuffer(PVDPATTERN pPattern, void **ppv, size_t cb)
2765{
2766 AssertPtrReturn(pPattern, VERR_INVALID_POINTER);
2767 AssertPtrReturn(ppv, VERR_INVALID_POINTER);
2768 AssertReturn(cb > 0, VERR_INVALID_PARAMETER);
2769
2770 if (cb > pPattern->cbPattern)
2771 return VERR_INVALID_PARAMETER;
2772
2773 *ppv = pPattern->pvPattern;
2774 return VINF_SUCCESS;
2775}
2776
2777/**
2778 * Executes the given script.
2779 *
2780 * @param pszName The script name.
2781 * @param pszScript The script to execute.
2782 */
2783static void tstVDIoScriptExec(const char *pszName, const char *pszScript)
2784{
2785 int rc = VINF_SUCCESS;
2786 VDTESTGLOB GlobTest; /**< Global test data. */
2787
2788 memset(&GlobTest, 0, sizeof(VDTESTGLOB));
2789 RTListInit(&GlobTest.ListFiles);
2790 RTListInit(&GlobTest.ListDisks);
2791 RTListInit(&GlobTest.ListPatterns);
2792 GlobTest.pszIoBackend = RTStrDup("memory");
2793 if (!GlobTest.pszIoBackend)
2794 {
2795 RTPrintf("Out of memory allocating I/O backend string\n");
2796 return;
2797 }
2798
2799 /* Init global test data. */
2800 GlobTest.VDIfError.pfnError = tstVDError;
2801 GlobTest.VDIfError.pfnMessage = tstVDMessage;
2802
2803 rc = VDInterfaceAdd(&GlobTest.VDIfError.Core, "tstVDIo_VDIError", VDINTERFACETYPE_ERROR,
2804 NULL, sizeof(VDINTERFACEERROR), &GlobTest.pInterfacesDisk);
2805 AssertRC(rc);
2806
2807 GlobTest.VDIfIo.pfnOpen = tstVDIoFileOpen;
2808 GlobTest.VDIfIo.pfnClose = tstVDIoFileClose;
2809 GlobTest.VDIfIo.pfnDelete = tstVDIoFileDelete;
2810 GlobTest.VDIfIo.pfnMove = tstVDIoFileMove;
2811 GlobTest.VDIfIo.pfnGetFreeSpace = tstVDIoFileGetFreeSpace;
2812 GlobTest.VDIfIo.pfnGetModificationTime = tstVDIoFileGetModificationTime;
2813 GlobTest.VDIfIo.pfnGetSize = tstVDIoFileGetSize;
2814 GlobTest.VDIfIo.pfnSetSize = tstVDIoFileSetSize;
2815 GlobTest.VDIfIo.pfnSetAllocationSize = tstVDIoFileSetAllocationSize;
2816 GlobTest.VDIfIo.pfnWriteSync = tstVDIoFileWriteSync;
2817 GlobTest.VDIfIo.pfnReadSync = tstVDIoFileReadSync;
2818 GlobTest.VDIfIo.pfnFlushSync = tstVDIoFileFlushSync;
2819 GlobTest.VDIfIo.pfnReadAsync = tstVDIoFileReadAsync;
2820 GlobTest.VDIfIo.pfnWriteAsync = tstVDIoFileWriteAsync;
2821 GlobTest.VDIfIo.pfnFlushAsync = tstVDIoFileFlushAsync;
2822
2823 rc = VDInterfaceAdd(&GlobTest.VDIfIo.Core, "tstVDIo_VDIIo", VDINTERFACETYPE_IO,
2824 &GlobTest, sizeof(VDINTERFACEIO), &GlobTest.pInterfacesImages);
2825 AssertRC(rc);
2826
2827 rc = RTTestCreate(pszName, &GlobTest.hTest);
2828 if (RT_SUCCESS(rc))
2829 {
2830 /* Init I/O backend. */
2831 rc = VDIoBackendCreate(&GlobTest.pIoBackend);
2832 if (RT_SUCCESS(rc))
2833 {
2834 VDSCRIPTCTX hScriptCtx = NULL;
2835 rc = VDScriptCtxCreate(&hScriptCtx);
2836 if (RT_SUCCESS(rc))
2837 {
2838 RTTEST_CHECK_RC_OK(GlobTest.hTest,
2839 VDScriptCtxCallbacksRegister(hScriptCtx, g_aScriptActions, g_cScriptActions, &GlobTest));
2840
2841 RTTestBanner(GlobTest.hTest);
2842 rc = VDScriptCtxLoadScript(hScriptCtx, pszScript);
2843 if (RT_FAILURE(rc))
2844 {
2845 RTPrintf("Loading the script failed rc=%Rrc\n", rc);
2846 }
2847 else
2848 rc = VDScriptCtxCallFn(hScriptCtx, "main", NULL, 0);
2849 VDScriptCtxDestroy(hScriptCtx);
2850 }
2851
2852 /* Clean up all leftover resources. */
2853 PVDPATTERN pPatternIt, pPatternItNext;
2854 RTListForEachSafe(&GlobTest.ListPatterns, pPatternIt, pPatternItNext, VDPATTERN, ListNode)
2855 {
2856 RTPrintf("Cleanup: Leftover pattern \"%s\", deleting...\n", pPatternIt->pszName);
2857 RTListNodeRemove(&pPatternIt->ListNode);
2858 RTMemFree(pPatternIt->pvPattern);
2859 RTStrFree(pPatternIt->pszName);
2860 RTMemFree(pPatternIt);
2861 }
2862
2863 PVDDISK pDiskIt, pDiskItNext;
2864 RTListForEachSafe(&GlobTest.ListDisks, pDiskIt, pDiskItNext, VDDISK, ListNode)
2865 {
2866 RTPrintf("Cleanup: Leftover disk \"%s\", deleting...\n", pDiskIt->pszName);
2867 RTListNodeRemove(&pDiskIt->ListNode);
2868 VDDestroy(pDiskIt->pVD);
2869 if (pDiskIt->pMemDiskVerify)
2870 {
2871 VDMemDiskDestroy(pDiskIt->pMemDiskVerify);
2872 RTCritSectDelete(&pDiskIt->CritSectVerify);
2873 }
2874 RTStrFree(pDiskIt->pszName);
2875 RTMemFree(pDiskIt);
2876 }
2877
2878 PVDFILE pFileIt, pFileItNext;
2879 RTListForEachSafe(&GlobTest.ListFiles, pFileIt, pFileItNext, VDFILE, Node)
2880 {
2881 RTPrintf("Cleanup: Leftover file \"%s\", deleting...\n", pFileIt->pszName);
2882 RTListNodeRemove(&pFileIt->Node);
2883 VDIoBackendStorageDestroy(pFileIt->pIoStorage);
2884 RTStrFree(pFileIt->pszName);
2885 RTMemFree(pFileIt);
2886 }
2887
2888 VDIoBackendDestroy(GlobTest.pIoBackend);
2889 }
2890 else
2891 RTPrintf("Creating the I/O backend failed rc=%Rrc\n", rc);
2892
2893 RTTestSummaryAndDestroy(GlobTest.hTest);
2894 }
2895 else
2896 RTStrmPrintf(g_pStdErr, "tstVDIo: fatal error: RTTestCreate failed with rc=%Rrc\n", rc);
2897
2898 RTStrFree(GlobTest.pszIoBackend);
2899}
2900
2901/**
2902 * Executes the given I/O script using the new scripting engine.
2903 *
2904 * @param pcszFilename The script to execute.
2905 */
2906static void tstVDIoScriptRun(const char *pcszFilename)
2907{
2908 int rc = VINF_SUCCESS;
2909 void *pvFile = NULL;
2910 size_t cbFile = 0;
2911
2912 rc = RTFileReadAll(pcszFilename, &pvFile, &cbFile);
2913 if (RT_SUCCESS(rc))
2914 {
2915 char *pszScript = RTStrDupN((char *)pvFile, cbFile);
2916 RTFileReadAllFree(pvFile, cbFile);
2917
2918 AssertPtr(pszScript);
2919 tstVDIoScriptExec(pcszFilename, pszScript);
2920 RTStrFree(pszScript);
2921 }
2922 else
2923 RTPrintf("Opening the script failed: %Rrc\n", rc);
2924
2925}
2926
2927/**
2928 * Run builtin tests.
2929 */
2930static void tstVDIoRunBuiltinTests(void)
2931{
2932 /* 32bit hosts are excluded because of the 4GB address space. */
2933#if HC_ARCH_BITS == 32
2934 RTStrmPrintf(g_pStdErr, "tstVDIo: Running on a 32bit host is not supported for the builtin tests, skipping\n");
2935 return;
2936#else
2937 /*
2938 * We need quite a bit of RAM for the builtin tests. Skip it if there
2939 * is not enough free RAM available.
2940 */
2941 uint64_t cbFree = 0;
2942 int rc = RTSystemQueryAvailableRam(&cbFree);
2943 if ( RT_FAILURE(rc)
2944 || cbFree < (UINT64_C(6) * _1G))
2945 {
2946 RTStrmPrintf(g_pStdErr, "tstVDIo: fatal error: Failed to query available RAM or not enough available, skipping (rc=%Rrc cbFree=%llu)\n",
2947 rc, cbFree);
2948 return;
2949 }
2950
2951 for (unsigned i = 0; i < g_cVDIoTests; i++)
2952 {
2953 char *pszScript = RTStrDupN((const char *)g_aVDIoTests[i].pch, g_aVDIoTests[i].cb);
2954
2955 AssertPtr(pszScript);
2956 tstVDIoScriptExec(g_aVDIoTests[i].pszName, pszScript);
2957 RTStrFree(pszScript);
2958 }
2959#endif
2960}
2961
2962/**
2963 * Shows help message.
2964 */
2965static void printUsage(void)
2966{
2967 RTPrintf("Usage:\n"
2968 "--script <filename> Script to execute\n");
2969}
2970
2971static const RTGETOPTDEF g_aOptions[] =
2972{
2973 { "--script", 's', RTGETOPT_REQ_STRING },
2974 { "--help", 'h', RTGETOPT_REQ_NOTHING }
2975};
2976
2977int main(int argc, char *argv[])
2978{
2979 RTR3InitExe(argc, &argv, 0);
2980 int rc;
2981 RTGETOPTUNION ValueUnion;
2982 RTGETOPTSTATE GetState;
2983 char c;
2984
2985 rc = VDInit();
2986 if (RT_FAILURE(rc))
2987 return RTEXITCODE_FAILURE;
2988
2989 if (argc == 1)
2990 {
2991 tstVDIoRunBuiltinTests();
2992 return RTEXITCODE_SUCCESS;
2993 }
2994
2995 RTGetOptInit(&GetState, argc, argv, g_aOptions,
2996 RT_ELEMENTS(g_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
2997
2998 while ( RT_SUCCESS(rc)
2999 && (c = RTGetOpt(&GetState, &ValueUnion)))
3000 {
3001 switch (c)
3002 {
3003 case 's':
3004 tstVDIoScriptRun(ValueUnion.psz);
3005 break;
3006 case 'h':
3007 printUsage();
3008 break;
3009 default: /* Default is to run built in tests if no arguments are given (automated testing). */
3010 tstVDIoRunBuiltinTests();
3011 }
3012 }
3013
3014 rc = VDShutdown();
3015 if (RT_FAILURE(rc))
3016 RTPrintf("tstVDIo: unloading backends failed! rc=%Rrc\n", rc);
3017
3018 return RTEXITCODE_SUCCESS;
3019}
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