VirtualBox

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

Last change on this file since 72293 was 70777, checked in by vboxsync, 7 years ago

Storage/tstVDIo: Allow loading plugins (like the crypto plugin) for testing

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 97.6 KB
Line 
1/* $Id: tstVDIo.cpp 70777 2018-01-28 22:02:45Z vboxsync $ */
2/** @file
3 *
4 * VBox HDD container test utility - I/O replay.
5 */
6
7/*
8 * Copyright (C) 2011-2017 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18#define LOGGROUP LOGGROUP_DEFAULT
19#include <VBox/vd.h>
20#include <VBox/err.h>
21#include <VBox/log.h>
22#include <iprt/asm.h>
23#include <iprt/string.h>
24#include <iprt/stream.h>
25#include <iprt/mem.h>
26#include <iprt/initterm.h>
27#include <iprt/getopt.h>
28#include <iprt/list.h>
29#include <iprt/ctype.h>
30#include <iprt/semaphore.h>
31#include <iprt/thread.h>
32#include <iprt/rand.h>
33#include <iprt/critsect.h>
34#include <iprt/test.h>
35#include <iprt/system.h>
36
37#ifdef VBOX_TSTVDIO_WITH_LOG_REPLAY
38# include <VBox/vddbg.h>
39#endif
40
41#include "VDMemDisk.h"
42#include "VDIoBackend.h"
43#include "VDIoRnd.h"
44
45#include "VDScript.h"
46#include "BuiltinTests.h"
47
48/** forward declaration for the global test data pointer. */
49typedef struct VDTESTGLOB *PVDTESTGLOB;
50
51/**
52 * A virtual file backed by memory.
53 */
54typedef struct VDFILE
55{
56 /** Pointer to the next file. */
57 RTLISTNODE Node;
58 /** Name of the file. */
59 char *pszName;
60 /** Storage backing the file. */
61 PVDIOSTORAGE pIoStorage;
62 /** Flag whether the file is read locked. */
63 bool fReadLock;
64 /** Flag whether the file is write locked. */
65 bool fWriteLock;
66 /** Statistics: Number of reads. */
67 unsigned cReads;
68 /** Statistics: Number of writes. */
69 unsigned cWrites;
70 /** Statistics: Number of flushes. */
71 unsigned cFlushes;
72 /** Statistics: Number of async reads. */
73 unsigned cAsyncReads;
74 /** Statistics: Number of async writes. */
75 unsigned cAsyncWrites;
76 /** Statistics: Number of async flushes. */
77 unsigned cAsyncFlushes;
78} VDFILE, *PVDFILE;
79
80/**
81 * VD storage object.
82 */
83typedef struct VDSTORAGE
84{
85 /** Pointer to the file. */
86 PVDFILE pFile;
87 /** Completion callback of the VD layer. */
88 PFNVDCOMPLETED pfnComplete;
89} VDSTORAGE, *PVDSTORAGE;
90
91/**
92 * A virtual disk.
93 */
94typedef struct VDDISK
95{
96 /** List node. */
97 RTLISTNODE ListNode;
98 /** Name of the disk handle for identification. */
99 char *pszName;
100 /** HDD handle to operate on. */
101 PVDISK pVD;
102 /** Memory disk used for data verification. */
103 PVDMEMDISK pMemDiskVerify;
104 /** Critical section to serialize access to the memory disk. */
105 RTCRITSECT CritSectVerify;
106 /** Physical CHS Geometry. */
107 VDGEOMETRY PhysGeom;
108 /** Logical CHS geometry. */
109 VDGEOMETRY LogicalGeom;
110 /** Global test data. */
111 PVDTESTGLOB pTestGlob;
112} VDDISK, *PVDDISK;
113
114/**
115 * A data buffer with a pattern.
116 */
117typedef struct VDPATTERN
118{
119 /** List node. */
120 RTLISTNODE ListNode;
121 /** Name of the pattern. */
122 char *pszName;
123 /** Size of the pattern. */
124 size_t cbPattern;
125 /** Pointer to the buffer containing the pattern. */
126 void *pvPattern;
127} VDPATTERN, *PVDPATTERN;
128
129/**
130 * Global VD test state.
131 */
132typedef struct VDTESTGLOB
133{
134 /** List of active virtual disks. */
135 RTLISTNODE ListDisks;
136 /** Head of the active file list. */
137 RTLISTNODE ListFiles;
138 /** Head of the pattern list. */
139 RTLISTNODE ListPatterns;
140 /** I/O backend, common data. */
141 PVDIOBACKEND pIoBackend;
142 /** Error interface. */
143 VDINTERFACEERROR VDIfError;
144 /** Pointer to the per disk interface list. */
145 PVDINTERFACE pInterfacesDisk;
146 /** I/O interface. */
147 VDINTERFACEIO VDIfIo;
148 /** Pointer to the per image interface list. */
149 PVDINTERFACE pInterfacesImages;
150 /** I/O RNG handle. */
151 PVDIORND pIoRnd;
152 /** Current storage backend to use. */
153 char *pszIoBackend;
154 /** Testcase handle. */
155 RTTEST hTest;
156} VDTESTGLOB;
157
158/**
159 * Transfer direction.
160 */
161typedef enum TSTVDIOREQTXDIR
162{
163 TSTVDIOREQTXDIR_READ = 0,
164 TSTVDIOREQTXDIR_WRITE,
165 TSTVDIOREQTXDIR_FLUSH,
166 TSTVDIOREQTXDIR_DISCARD
167} TSTVDIOREQTXDIR;
168
169/**
170 * I/O request.
171 */
172typedef struct TSTVDIOREQ
173{
174 /** Transfer type. */
175 TSTVDIOREQTXDIR enmTxDir;
176 /** slot index. */
177 unsigned idx;
178 /** Start offset. */
179 uint64_t off;
180 /** Size to transfer. */
181 size_t cbReq;
182 /** S/G Buffer */
183 RTSGBUF SgBuf;
184 /** Flag whether the request is outstanding or not. */
185 volatile bool fOutstanding;
186 /** Buffer to use for reads. */
187 void *pvBufRead;
188 /** Contiguous buffer pointer backing the segments. */
189 void *pvBuf;
190 /** Opaque user data. */
191 void *pvUser;
192 /** Number of segments used for the data buffer. */
193 uint32_t cSegs;
194 /** Array of data segments. */
195 RTSGSEG aSegs[10];
196} TSTVDIOREQ, *PTSTVDIOREQ;
197
198/**
199 * I/O test data.
200 */
201typedef struct VDIOTEST
202{
203 /** Start offset. */
204 uint64_t offStart;
205 /** End offset. */
206 uint64_t offEnd;
207 /** Flag whether random or sequential access is wanted */
208 bool fRandomAccess;
209 /** Block size. */
210 size_t cbBlkIo;
211 /** Number of bytes to transfer. */
212 uint64_t cbIo;
213 /** Chance in percent to get a write. */
214 unsigned uWriteChance;
215 /** Maximum number of segments to create for one request. */
216 uint32_t cSegsMax;
217 /** Pointer to the I/O data generator. */
218 PVDIORND pIoRnd;
219 /** Pointer to the data pattern to use. */
220 PVDPATTERN pPattern;
221 /** Data dependent on the I/O mode (sequential or random). */
222 union
223 {
224 /** Next offset for sequential access. */
225 uint64_t offNext;
226 /** Data for random acess. */
227 struct
228 {
229 /** Number of valid entries in the bitmap. */
230 uint32_t cBlocks;
231 /** Pointer to the bitmap marking accessed blocks. */
232 uint8_t *pbMapAccessed;
233 /** Number of unaccessed blocks. */
234 uint32_t cBlocksLeft;
235 } Rnd;
236 } u;
237} VDIOTEST, *PVDIOTEST;
238
239static DECLCALLBACK(int) vdScriptHandlerCreate(PVDSCRIPTARG paScriptArgs, void *pvUser);
240static DECLCALLBACK(int) vdScriptHandlerOpen(PVDSCRIPTARG paScriptArgs, void *pvUser);
241static DECLCALLBACK(int) vdScriptHandlerIo(PVDSCRIPTARG paScriptArgs, void *pvUser);
242static DECLCALLBACK(int) vdScriptHandlerFlush(PVDSCRIPTARG paScriptArgs, void *pvUser);
243static DECLCALLBACK(int) vdScriptHandlerMerge(PVDSCRIPTARG paScriptArgs, void *pvUser);
244static DECLCALLBACK(int) vdScriptHandlerCompact(PVDSCRIPTARG paScriptArgs, void *pvUser);
245static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDSCRIPTARG paScriptArgs, void *pvUser);
246static DECLCALLBACK(int) vdScriptHandlerCopy(PVDSCRIPTARG paScriptArgs, void *pvUser);
247static DECLCALLBACK(int) vdScriptHandlerClose(PVDSCRIPTARG paScriptArgs, void *pvUser);
248static DECLCALLBACK(int) vdScriptHandlerPrintFileSize(PVDSCRIPTARG paScriptArgs, void *pvUser);
249static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDSCRIPTARG paScriptArgs, void *pvUser);
250static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser);
251static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDSCRIPTARG paScriptArgs, void *pvUser);
252static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDSCRIPTARG paScriptArgs, void *pvUser);
253static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser);
254static DECLCALLBACK(int) vdScriptHandlerSleep(PVDSCRIPTARG paScriptArgs, void *pvUser);
255static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDSCRIPTARG paScriptArgs, void *pvUser);
256static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDSCRIPTARG paScriptArgs, void *pvUser);
257static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDSCRIPTARG paScriptArgs, void *pvUser);
258static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDSCRIPTARG paScriptArgs, void *pvUser);
259static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDSCRIPTARG paScriptArgs, void *pvUser);
260static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDSCRIPTARG paScriptArgs, void *pvUser);
261static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser);
262static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser);
263static DECLCALLBACK(int) vdScriptHandlerResize(PVDSCRIPTARG paScriptArgs, void *pvUser);
264static DECLCALLBACK(int) vdScriptHandlerSetFileBackend(PVDSCRIPTARG paScriptArgs, void *pvUser);
265static DECLCALLBACK(int) vdScriptHandlerLoadPlugin(PVDSCRIPTARG paScriptArgs, void *pvUser);
266
267#ifdef VBOX_TSTVDIO_WITH_LOG_REPLAY
268static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDSCRIPTARG paScriptArgs, void *pvUser);
269#endif
270
271/* create action */
272const VDSCRIPTTYPE g_aArgCreate[] =
273{
274 VDSCRIPTTYPE_STRING,
275 VDSCRIPTTYPE_STRING,
276 VDSCRIPTTYPE_STRING,
277 VDSCRIPTTYPE_STRING,
278 VDSCRIPTTYPE_STRING,
279 VDSCRIPTTYPE_UINT64,
280 VDSCRIPTTYPE_BOOL,
281 VDSCRIPTTYPE_BOOL
282};
283
284/* open action */
285const VDSCRIPTTYPE g_aArgOpen[] =
286{
287 VDSCRIPTTYPE_STRING, /* disk */
288 VDSCRIPTTYPE_STRING, /* name */
289 VDSCRIPTTYPE_STRING, /* backend */
290 VDSCRIPTTYPE_BOOL, /* async */
291 VDSCRIPTTYPE_BOOL, /* shareable */
292 VDSCRIPTTYPE_BOOL, /* readonly */
293 VDSCRIPTTYPE_BOOL, /* discard */
294 VDSCRIPTTYPE_BOOL, /* ignoreflush */
295 VDSCRIPTTYPE_BOOL, /* honorsame */
296};
297
298/* I/O action */
299const VDSCRIPTTYPE g_aArgIo[] =
300{
301 VDSCRIPTTYPE_STRING, /* disk */
302 VDSCRIPTTYPE_BOOL, /* async */
303 VDSCRIPTTYPE_UINT32, /* max-reqs */
304 VDSCRIPTTYPE_STRING, /* mode */
305 VDSCRIPTTYPE_UINT64, /* size */
306 VDSCRIPTTYPE_UINT64, /* blocksize */
307 VDSCRIPTTYPE_UINT64, /* offStart */
308 VDSCRIPTTYPE_UINT64, /* offEnd */
309 VDSCRIPTTYPE_UINT32, /* writes */
310 VDSCRIPTTYPE_STRING /* pattern */
311};
312
313/* flush action */
314const VDSCRIPTTYPE g_aArgFlush[] =
315{
316 VDSCRIPTTYPE_STRING, /* disk */
317 VDSCRIPTTYPE_BOOL /* async */
318};
319
320/* merge action */
321const VDSCRIPTTYPE g_aArgMerge[] =
322{
323 VDSCRIPTTYPE_STRING, /* disk */
324 VDSCRIPTTYPE_UINT32, /* from */
325 VDSCRIPTTYPE_UINT32 /* to */
326};
327
328/* Compact a disk */
329const VDSCRIPTTYPE g_aArgCompact[] =
330{
331 VDSCRIPTTYPE_STRING, /* disk */
332 VDSCRIPTTYPE_UINT32 /* image */
333};
334
335/* Discard a part of a disk */
336const VDSCRIPTTYPE g_aArgDiscard[] =
337{
338 VDSCRIPTTYPE_STRING, /* disk */
339 VDSCRIPTTYPE_BOOL, /* async */
340 VDSCRIPTTYPE_STRING /* ranges */
341};
342
343/* Compact a disk */
344const VDSCRIPTTYPE g_aArgCopy[] =
345{
346 VDSCRIPTTYPE_STRING, /* diskfrom */
347 VDSCRIPTTYPE_STRING, /* diskto */
348 VDSCRIPTTYPE_UINT32, /* imagefrom */
349 VDSCRIPTTYPE_STRING, /* backend */
350 VDSCRIPTTYPE_STRING, /* filename */
351 VDSCRIPTTYPE_BOOL, /* movebyrename */
352 VDSCRIPTTYPE_UINT64, /* size */
353 VDSCRIPTTYPE_UINT32, /* fromsame */
354 VDSCRIPTTYPE_UINT32 /* tosame */
355};
356
357/* close action */
358const VDSCRIPTTYPE g_aArgClose[] =
359{
360 VDSCRIPTTYPE_STRING, /* disk */
361 VDSCRIPTTYPE_STRING, /* mode */
362 VDSCRIPTTYPE_BOOL /* delete */
363};
364
365/* print file size action */
366const VDSCRIPTTYPE g_aArgPrintFileSize[] =
367{
368 VDSCRIPTTYPE_STRING, /* disk */
369 VDSCRIPTTYPE_UINT32 /* image */
370};
371
372#ifdef VBOX_TSTVDIO_WITH_LOG_REPLAY
373/* print file size action */
374const VDSCRIPTTYPE g_aArgIoLogReplay[] =
375{
376 VDSCRIPTTYPE_STRING, /* disk */
377 VDSCRIPTTYPE_STRING /* iolog */
378};
379#endif
380
381/* I/O RNG create action */
382const VDSCRIPTTYPE g_aArgIoRngCreate[] =
383{
384 VDSCRIPTTYPE_UINT32, /* size */
385 VDSCRIPTTYPE_STRING, /* mode */
386 VDSCRIPTTYPE_UINT32, /* seed */
387};
388
389/* I/O pattern create action */
390const VDSCRIPTTYPE g_aArgIoPatternCreateFromNumber[] =
391{
392 VDSCRIPTTYPE_STRING, /* name */
393 VDSCRIPTTYPE_UINT32, /* size */
394 VDSCRIPTTYPE_UINT32 /* pattern */
395};
396
397/* I/O pattern create action */
398const VDSCRIPTTYPE g_aArgIoPatternCreateFromFile[] =
399{
400 VDSCRIPTTYPE_STRING, /* name */
401 VDSCRIPTTYPE_STRING /* file */
402};
403
404/* I/O pattern destroy action */
405const VDSCRIPTTYPE g_aArgIoPatternDestroy[] =
406{
407 VDSCRIPTTYPE_STRING /* name */
408};
409
410/* Sleep */
411const VDSCRIPTTYPE g_aArgSleep[] =
412{
413 VDSCRIPTTYPE_UINT32 /* time */
414};
415
416/* Dump memory file */
417const VDSCRIPTTYPE g_aArgDumpFile[] =
418{
419 VDSCRIPTTYPE_STRING, /* file */
420 VDSCRIPTTYPE_STRING /* path */
421};
422
423/* Create virtual disk handle */
424const VDSCRIPTTYPE g_aArgCreateDisk[] =
425{
426 VDSCRIPTTYPE_STRING, /* name */
427 VDSCRIPTTYPE_BOOL /* verify */
428};
429
430/* Create virtual disk handle */
431const VDSCRIPTTYPE g_aArgDestroyDisk[] =
432{
433 VDSCRIPTTYPE_STRING /* name */
434};
435
436/* Compare virtual disks */
437const VDSCRIPTTYPE g_aArgCompareDisks[] =
438{
439 VDSCRIPTTYPE_STRING, /* disk1 */
440 VDSCRIPTTYPE_STRING /* disk2 */
441};
442
443/* Dump disk info */
444const VDSCRIPTTYPE g_aArgDumpDiskInfo[] =
445{
446 VDSCRIPTTYPE_STRING /* disk */
447};
448
449/* Print message */
450const VDSCRIPTTYPE g_aArgPrintMsg[] =
451{
452 VDSCRIPTTYPE_STRING /* msg */
453};
454
455/* Show statistics */
456const VDSCRIPTTYPE g_aArgShowStatistics[] =
457{
458 VDSCRIPTTYPE_STRING /* file */
459};
460
461/* Reset statistics */
462const VDSCRIPTTYPE g_aArgResetStatistics[] =
463{
464 VDSCRIPTTYPE_STRING /* file */
465};
466
467/* Resize disk. */
468const VDSCRIPTTYPE g_aArgResize[] =
469{
470 VDSCRIPTTYPE_STRING, /* disk */
471 VDSCRIPTTYPE_UINT64 /* size */
472};
473
474/* Set file backend. */
475const VDSCRIPTTYPE g_aArgSetFileBackend[] =
476{
477 VDSCRIPTTYPE_STRING /* new file backend */
478};
479
480/* Load plugin. */
481const VDSCRIPTTYPE g_aArgLoadPlugin[] =
482{
483 VDSCRIPTTYPE_STRING /* plugin name */
484};
485
486const VDSCRIPTCALLBACK g_aScriptActions[] =
487{
488 /* pcszFnName enmTypeReturn paArgDesc cArgDescs pfnHandler */
489 {"create", VDSCRIPTTYPE_VOID, g_aArgCreate, RT_ELEMENTS(g_aArgCreate), vdScriptHandlerCreate},
490 {"open", VDSCRIPTTYPE_VOID, g_aArgOpen, RT_ELEMENTS(g_aArgOpen), vdScriptHandlerOpen},
491 {"io", VDSCRIPTTYPE_VOID, g_aArgIo, RT_ELEMENTS(g_aArgIo), vdScriptHandlerIo},
492 {"flush", VDSCRIPTTYPE_VOID, g_aArgFlush, RT_ELEMENTS(g_aArgFlush), vdScriptHandlerFlush},
493 {"close", VDSCRIPTTYPE_VOID, g_aArgClose, RT_ELEMENTS(g_aArgClose), vdScriptHandlerClose},
494 {"printfilesize", VDSCRIPTTYPE_VOID, g_aArgPrintFileSize, RT_ELEMENTS(g_aArgPrintFileSize), vdScriptHandlerPrintFileSize},
495#ifdef VBOX_TSTVDIO_WITH_LOG_REPLAY
496 {"ioreplay", VDSCRIPTTYPE_VOID, g_aArgIoLogReplay, RT_ELEMENTS(g_aArgIoLogReplay), vdScriptHandlerIoLogReplay},
497#endif
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
1410#ifdef VBOX_TSTVDIO_WITH_LOG_REPLAY
1411static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDSCRIPTARG paScriptArgs, void *pvUser)
1412{
1413 int rc = VINF_SUCCESS;
1414 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1415 PVDDISK pDisk = NULL;
1416 const char *pcszDisk = paScriptArgs[0].psz;
1417 const char *pcszIoLog = paScriptArgs[1].psz;
1418
1419 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1420 if (pDisk)
1421 {
1422 VDIOLOGGER hIoLogger;
1423
1424 rc = VDDbgIoLogOpen(&hIoLogger, pcszIoLog);
1425 if (RT_SUCCESS(rc))
1426 {
1427 uint32_t fIoLogFlags;
1428 VDIOLOGEVENT enmEvent;
1429 void *pvBuf = NULL;
1430 size_t cbBuf = 0;
1431
1432 fIoLogFlags = VDDbgIoLogGetFlags(hIoLogger);
1433
1434 /* Loop through events. */
1435 rc = VDDbgIoLogEventTypeGetNext(hIoLogger, &enmEvent);
1436 while ( RT_SUCCESS(rc)
1437 && enmEvent != VDIOLOGEVENT_END)
1438 {
1439 VDDBGIOLOGREQ enmReq = VDDBGIOLOGREQ_INVALID;
1440 uint64_t idEvent = 0;
1441 bool fAsync = false;
1442 uint64_t off = 0;
1443 size_t cbIo = 0;
1444 Assert(enmEvent == VDIOLOGEVENT_START);
1445
1446 rc = VDDbgIoLogReqTypeGetNext(hIoLogger, &enmReq);
1447 if (RT_FAILURE(rc))
1448 break;
1449
1450 switch (enmReq)
1451 {
1452 case VDDBGIOLOGREQ_READ:
1453 {
1454 rc = VDDbgIoLogEventGetStart(hIoLogger, &idEvent, &fAsync,
1455 &off, &cbIo, 0, NULL);
1456 if ( RT_SUCCESS(rc)
1457 && cbIo > cbBuf)
1458 {
1459 pvBuf = RTMemRealloc(pvBuf, cbIo);
1460 if (pvBuf)
1461 cbBuf = cbIo;
1462 else
1463 rc = VERR_NO_MEMORY;
1464 }
1465
1466 if ( RT_SUCCESS(rc)
1467 && !fAsync)
1468 rc = VDRead(pDisk->pVD, off, pvBuf, cbIo);
1469 else if (RT_SUCCESS(rc))
1470 rc = VERR_NOT_SUPPORTED;
1471 break;
1472 }
1473 case VDDBGIOLOGREQ_WRITE:
1474 {
1475 rc = VDDbgIoLogEventGetStart(hIoLogger, &idEvent, &fAsync,
1476 &off, &cbIo, cbBuf, pvBuf);
1477 if (rc == VERR_BUFFER_OVERFLOW)
1478 {
1479 pvBuf = RTMemRealloc(pvBuf, cbIo);
1480 if (pvBuf)
1481 {
1482 cbBuf = cbIo;
1483 rc = VDDbgIoLogEventGetStart(hIoLogger, &idEvent, &fAsync,
1484 &off, &cbIo, cbBuf, pvBuf);
1485 }
1486 else
1487 rc = VERR_NO_MEMORY;
1488 }
1489
1490 if ( RT_SUCCESS(rc)
1491 && !fAsync)
1492 rc = VDWrite(pDisk->pVD, off, pvBuf, cbIo);
1493 else if (RT_SUCCESS(rc))
1494 rc = VERR_NOT_SUPPORTED;
1495 break;
1496 }
1497 case VDDBGIOLOGREQ_FLUSH:
1498 {
1499 rc = VDDbgIoLogEventGetStart(hIoLogger, &idEvent, &fAsync,
1500 &off, &cbIo, 0, NULL);
1501 if ( RT_SUCCESS(rc)
1502 && !fAsync)
1503 rc = VDFlush(pDisk->pVD);
1504 else if (RT_SUCCESS(rc))
1505 rc = VERR_NOT_SUPPORTED;
1506 break;
1507 }
1508 case VDDBGIOLOGREQ_DISCARD:
1509 {
1510 PRTRANGE paRanges = NULL;
1511 unsigned cRanges = 0;
1512
1513 rc = VDDbgIoLogEventGetStartDiscard(hIoLogger, &idEvent, &fAsync,
1514 &paRanges, &cRanges);
1515 if ( RT_SUCCESS(rc)
1516 && !fAsync)
1517 {
1518 rc = VDDiscardRanges(pDisk->pVD, paRanges, cRanges);
1519 RTMemFree(paRanges);
1520 }
1521 else if (RT_SUCCESS(rc))
1522 rc = VERR_NOT_SUPPORTED;
1523 break;
1524 }
1525 default:
1526 AssertMsgFailed(("Invalid request type %d\n", enmReq));
1527 }
1528
1529 if (RT_SUCCESS(rc))
1530 {
1531 /* Get matching complete event. */
1532 rc = VDDbgIoLogEventTypeGetNext(hIoLogger, &enmEvent);
1533 if (RT_SUCCESS(rc))
1534 {
1535 uint64_t idEvtComplete;
1536 int rcReq;
1537 uint64_t msDuration;
1538
1539 Assert(enmEvent == VDIOLOGEVENT_COMPLETE);
1540 rc = VDDbgIoLogEventGetComplete(hIoLogger, &idEvtComplete, &rcReq,
1541 &msDuration, &cbIo, cbBuf, pvBuf);
1542 Assert(RT_FAILURE(rc) || idEvtComplete == idEvent);
1543 }
1544 }
1545
1546 if (RT_SUCCESS(rc))
1547 rc = VDDbgIoLogEventTypeGetNext(hIoLogger, &enmEvent);
1548 }
1549
1550 VDDbgIoLogDestroy(hIoLogger);
1551 }
1552 }
1553 else
1554 rc = VERR_NOT_FOUND;
1555
1556 return rc;
1557}
1558#endif /* VBOX_TSTVDIO_WITH_LOG_REPLAY */
1559
1560
1561static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDSCRIPTARG paScriptArgs, void *pvUser)
1562{
1563 int rc = VINF_SUCCESS;
1564 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1565 size_t cbPattern = (size_t)paScriptArgs[0].u64;
1566 const char *pcszSeeder = paScriptArgs[1].psz;
1567 uint64_t uSeed = paScriptArgs[2].u64;
1568
1569 if (pGlob->pIoRnd)
1570 {
1571 RTPrintf("I/O RNG already exists\n");
1572 rc = VERR_INVALID_STATE;
1573 }
1574 else
1575 {
1576 uint64_t uSeedToUse = 0;
1577
1578 if (!RTStrICmp(pcszSeeder, "manual"))
1579 uSeedToUse = uSeed;
1580 else if (!RTStrICmp(pcszSeeder, "time"))
1581 uSeedToUse = RTTimeSystemMilliTS();
1582 else if (!RTStrICmp(pcszSeeder, "system"))
1583 {
1584 RTRAND hRand;
1585 rc = RTRandAdvCreateSystemTruer(&hRand);
1586 if (RT_SUCCESS(rc))
1587 {
1588 RTRandAdvBytes(hRand, &uSeedToUse, sizeof(uSeedToUse));
1589 RTRandAdvDestroy(hRand);
1590 }
1591 }
1592
1593 if (RT_SUCCESS(rc))
1594 rc = VDIoRndCreate(&pGlob->pIoRnd, cbPattern, uSeed);
1595 }
1596
1597 return rc;
1598}
1599
1600static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser)
1601{
1602 RT_NOREF1(paScriptArgs);
1603 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1604
1605 if (pGlob->pIoRnd)
1606 {
1607 VDIoRndDestroy(pGlob->pIoRnd);
1608 pGlob->pIoRnd = NULL;
1609 }
1610 else
1611 RTPrintf("WARNING: No I/O RNG active, faulty script. Continuing\n");
1612
1613 return VINF_SUCCESS;
1614}
1615
1616static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDSCRIPTARG paScriptArgs, void *pvUser)
1617{
1618 int rc = VINF_SUCCESS;
1619 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1620 const char *pcszName = paScriptArgs[0].psz;
1621 size_t cbPattern = (size_t)paScriptArgs[1].u64;
1622 uint64_t u64Pattern = paScriptArgs[2].u64;
1623
1624 PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName);
1625 if (!pPattern)
1626 {
1627 pPattern = tstVDIoPatternCreate(pcszName, RT_ALIGN_Z(cbPattern, sizeof(uint64_t)));
1628 if (pPattern)
1629 {
1630 /* Fill the buffer. */
1631 void *pv = pPattern->pvPattern;
1632
1633 while (pPattern->cbPattern > 0)
1634 {
1635 *((uint64_t*)pv) = u64Pattern;
1636 pPattern->cbPattern -= sizeof(uint64_t);
1637 pv = (uint64_t *)pv + 1;
1638 }
1639 pPattern->cbPattern = cbPattern; /* Set to the desired size. (could be unaligned) */
1640
1641 RTListAppend(&pGlob->ListPatterns, &pPattern->ListNode);
1642 }
1643 else
1644 rc = VERR_NO_MEMORY;
1645 }
1646 else
1647 rc = VERR_ALREADY_EXISTS;
1648
1649 return rc;
1650}
1651
1652static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDSCRIPTARG paScriptArgs, void *pvUser)
1653{
1654 int rc = VINF_SUCCESS;
1655 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1656 const char *pcszName = paScriptArgs[0].psz;
1657 const char *pcszFile = paScriptArgs[1].psz;
1658
1659 PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName);
1660 if (!pPattern)
1661 {
1662 RTFILE hFile;
1663 uint64_t cbPattern = 0;
1664
1665 rc = RTFileOpen(&hFile, pcszFile, RTFILE_O_DENY_NONE | RTFILE_O_OPEN | RTFILE_O_READ);
1666 if (RT_SUCCESS(rc))
1667 {
1668 rc = RTFileGetSize(hFile, &cbPattern);
1669 if (RT_SUCCESS(rc))
1670 {
1671 pPattern = tstVDIoPatternCreate(pcszName, (size_t)cbPattern);
1672 if (pPattern)
1673 {
1674 rc = RTFileRead(hFile, pPattern->pvPattern, (size_t)cbPattern, NULL);
1675 if (RT_SUCCESS(rc))
1676 RTListAppend(&pGlob->ListPatterns, &pPattern->ListNode);
1677 else
1678 {
1679 RTMemFree(pPattern->pvPattern);
1680 RTStrFree(pPattern->pszName);
1681 RTMemFree(pPattern);
1682 }
1683 }
1684 else
1685 rc = VERR_NO_MEMORY;
1686 }
1687 RTFileClose(hFile);
1688 }
1689 }
1690 else
1691 rc = VERR_ALREADY_EXISTS;
1692
1693 return rc;
1694}
1695
1696static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser)
1697{
1698 int rc = VINF_SUCCESS;
1699 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1700 const char *pcszName = paScriptArgs[0].psz;
1701
1702 PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName);
1703 if (pPattern)
1704 {
1705 RTListNodeRemove(&pPattern->ListNode);
1706 RTMemFree(pPattern->pvPattern);
1707 RTStrFree(pPattern->pszName);
1708 RTMemFree(pPattern);
1709 }
1710 else
1711 rc = VERR_NOT_FOUND;
1712
1713 return rc;
1714}
1715
1716static DECLCALLBACK(int) vdScriptHandlerSleep(PVDSCRIPTARG paScriptArgs, void *pvUser)
1717{
1718 RT_NOREF1(pvUser);
1719 uint64_t cMillies = paScriptArgs[0].u64;
1720
1721 int rc = RTThreadSleep(cMillies);
1722 return rc;
1723}
1724
1725static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDSCRIPTARG paScriptArgs, void *pvUser)
1726{
1727 int rc = VINF_SUCCESS;
1728 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1729 const char *pcszFile = paScriptArgs[0].psz;
1730 const char *pcszPathToDump = paScriptArgs[1].psz;
1731
1732 /* Check for the file. */
1733 bool fFound = false;
1734 PVDFILE pIt;
1735 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1736 {
1737 if (!RTStrCmp(pIt->pszName, pcszFile))
1738 {
1739 fFound = true;
1740 break;
1741 }
1742 }
1743
1744 if (fFound)
1745 {
1746 RTPrintf("Dumping memory file %s to %s, this might take some time\n", pcszFile, pcszPathToDump);
1747 rc = VDIoBackendDumpToFile(pIt->pIoStorage, pcszPathToDump);
1748 rc = VERR_NOT_IMPLEMENTED;
1749 }
1750 else
1751 rc = VERR_FILE_NOT_FOUND;
1752
1753 return rc;
1754}
1755
1756static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDSCRIPTARG paScriptArgs, void *pvUser)
1757{
1758 int rc = VINF_SUCCESS;
1759 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1760 const char *pcszDisk = NULL;
1761 PVDDISK pDisk = NULL;
1762 bool fVerify = false;
1763
1764 pcszDisk = paScriptArgs[0].psz;
1765 fVerify = paScriptArgs[1].f;
1766
1767 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1768 if (pDisk)
1769 rc = VERR_ALREADY_EXISTS;
1770 else
1771 {
1772 pDisk = (PVDDISK)RTMemAllocZ(sizeof(VDDISK));
1773 if (pDisk)
1774 {
1775 pDisk->pTestGlob = pGlob;
1776 pDisk->pszName = RTStrDup(pcszDisk);
1777 if (pDisk->pszName)
1778 {
1779 rc = VINF_SUCCESS;
1780
1781 if (fVerify)
1782 {
1783 rc = VDMemDiskCreate(&pDisk->pMemDiskVerify, 0 /* Growing */);
1784 if (RT_SUCCESS(rc))
1785 {
1786 rc = RTCritSectInit(&pDisk->CritSectVerify);
1787 if (RT_FAILURE(rc))
1788 VDMemDiskDestroy(pDisk->pMemDiskVerify);
1789 }
1790 }
1791
1792 if (RT_SUCCESS(rc))
1793 {
1794 rc = VDCreate(pGlob->pInterfacesDisk, VDTYPE_HDD, &pDisk->pVD);
1795
1796 if (RT_SUCCESS(rc))
1797 RTListAppend(&pGlob->ListDisks, &pDisk->ListNode);
1798 else
1799 {
1800 if (fVerify)
1801 {
1802 RTCritSectDelete(&pDisk->CritSectVerify);
1803 VDMemDiskDestroy(pDisk->pMemDiskVerify);
1804 }
1805 RTStrFree(pDisk->pszName);
1806 }
1807 }
1808 }
1809 else
1810 rc = VERR_NO_MEMORY;
1811
1812 if (RT_FAILURE(rc))
1813 RTMemFree(pDisk);
1814 }
1815 else
1816 rc = VERR_NO_MEMORY;
1817 }
1818 return rc;
1819}
1820
1821static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDSCRIPTARG paScriptArgs, void *pvUser)
1822{
1823 int rc = VINF_SUCCESS;
1824 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1825 const char *pcszDisk = NULL;
1826 PVDDISK pDisk = NULL;
1827
1828 pcszDisk = paScriptArgs[0].psz;
1829
1830 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1831 if (pDisk)
1832 {
1833 RTListNodeRemove(&pDisk->ListNode);
1834 VDDestroy(pDisk->pVD);
1835 if (pDisk->pMemDiskVerify)
1836 {
1837 VDMemDiskDestroy(pDisk->pMemDiskVerify);
1838 RTCritSectDelete(&pDisk->CritSectVerify);
1839 }
1840 RTStrFree(pDisk->pszName);
1841 RTMemFree(pDisk);
1842 }
1843 else
1844 rc = VERR_NOT_FOUND;
1845
1846 return rc;
1847}
1848
1849static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDSCRIPTARG paScriptArgs, void *pvUser)
1850{
1851 int rc = VINF_SUCCESS;
1852 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1853 const char *pcszDisk1 = NULL;
1854 PVDDISK pDisk1 = NULL;
1855 const char *pcszDisk2 = NULL;
1856 PVDDISK pDisk2 = NULL;
1857
1858 pcszDisk1 = paScriptArgs[0].psz;
1859 pcszDisk2 = paScriptArgs[1].psz;
1860
1861 pDisk1 = tstVDIoGetDiskByName(pGlob, pcszDisk1);
1862 pDisk2 = tstVDIoGetDiskByName(pGlob, pcszDisk2);
1863
1864 if (pDisk1 && pDisk2)
1865 {
1866 uint8_t *pbBuf1 = (uint8_t *)RTMemAllocZ(16 * _1M);
1867 uint8_t *pbBuf2 = (uint8_t *)RTMemAllocZ(16 * _1M);
1868 if (pbBuf1 && pbBuf2)
1869 {
1870 uint64_t cbDisk1, cbDisk2;
1871 uint64_t uOffCur = 0;
1872
1873 cbDisk1 = VDGetSize(pDisk1->pVD, VD_LAST_IMAGE);
1874 cbDisk2 = VDGetSize(pDisk2->pVD, VD_LAST_IMAGE);
1875
1876 RTTestSub(pGlob->hTest, "Comparing two disks for equal content");
1877 if (cbDisk1 != cbDisk2)
1878 RTTestFailed(pGlob->hTest, "Disks differ in size %llu vs %llu\n", cbDisk1, cbDisk2);
1879 else
1880 {
1881 while (uOffCur < cbDisk1)
1882 {
1883 size_t cbRead = RT_MIN(cbDisk1, 16 * _1M);
1884
1885 rc = VDRead(pDisk1->pVD, uOffCur, pbBuf1, cbRead);
1886 if (RT_SUCCESS(rc))
1887 rc = VDRead(pDisk2->pVD, uOffCur, pbBuf2, cbRead);
1888
1889 if (RT_SUCCESS(rc))
1890 {
1891 if (memcmp(pbBuf1, pbBuf2, cbRead))
1892 {
1893 RTTestFailed(pGlob->hTest, "Disks differ at offset %llu\n", uOffCur);
1894 rc = VERR_DEV_IO_ERROR;
1895 break;
1896 }
1897 }
1898 else
1899 {
1900 RTTestFailed(pGlob->hTest, "Reading one disk at offset %llu failed\n", uOffCur);
1901 break;
1902 }
1903
1904 uOffCur += cbRead;
1905 cbDisk1 -= cbRead;
1906 }
1907 }
1908 RTMemFree(pbBuf1);
1909 RTMemFree(pbBuf2);
1910 }
1911 else
1912 {
1913 if (pbBuf1)
1914 RTMemFree(pbBuf1);
1915 if (pbBuf2)
1916 RTMemFree(pbBuf2);
1917 rc = VERR_NO_MEMORY;
1918 }
1919 }
1920 else
1921 rc = VERR_NOT_FOUND;
1922
1923 return rc;
1924}
1925
1926static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDSCRIPTARG paScriptArgs, void *pvUser)
1927{
1928 int rc = VINF_SUCCESS;
1929 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1930 const char *pcszDisk = NULL;
1931 PVDDISK pDisk = NULL;
1932
1933 pcszDisk = paScriptArgs[0].psz;
1934
1935 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1936
1937 if (pDisk)
1938 VDDumpImages(pDisk->pVD);
1939 else
1940 rc = VERR_NOT_FOUND;
1941
1942 return rc;
1943}
1944
1945static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDSCRIPTARG paScriptArgs, void *pvUser)
1946{
1947 RT_NOREF1(pvUser);
1948 RTPrintf("%s\n", paScriptArgs[0].psz);
1949 return VINF_SUCCESS;
1950}
1951
1952static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser)
1953{
1954 int rc = VINF_SUCCESS;
1955 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1956 const char *pcszFile = paScriptArgs[0].psz;
1957
1958 /* Check for the file. */
1959 bool fFound = false;
1960 PVDFILE pIt;
1961 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1962 {
1963 if (!RTStrCmp(pIt->pszName, pcszFile))
1964 {
1965 fFound = true;
1966 break;
1967 }
1968 }
1969
1970 if (fFound)
1971 {
1972 RTPrintf("Statistics %s: \n"
1973 " sync reads=%u writes=%u flushes=%u\n"
1974 " async reads=%u writes=%u flushes=%u\n",
1975 pcszFile,
1976 pIt->cReads, pIt->cWrites, pIt->cFlushes,
1977 pIt->cAsyncReads, pIt->cAsyncWrites, pIt->cAsyncFlushes);
1978 }
1979 else
1980 rc = VERR_FILE_NOT_FOUND;
1981
1982 return rc;
1983}
1984
1985static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser)
1986{
1987 int rc = VINF_SUCCESS;
1988 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1989 const char *pcszFile = paScriptArgs[0].psz;
1990
1991 /* Check for the file. */
1992 bool fFound = false;
1993 PVDFILE pIt;
1994 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1995 {
1996 if (!RTStrCmp(pIt->pszName, pcszFile))
1997 {
1998 fFound = true;
1999 break;
2000 }
2001 }
2002
2003 if (fFound)
2004 {
2005 pIt->cReads = 0;
2006 pIt->cWrites = 0;
2007 pIt->cFlushes = 0;
2008
2009 pIt->cAsyncReads = 0;
2010 pIt->cAsyncWrites = 0;
2011 pIt->cAsyncFlushes = 0;
2012 }
2013 else
2014 rc = VERR_FILE_NOT_FOUND;
2015
2016 return rc;
2017}
2018
2019static DECLCALLBACK(int) vdScriptHandlerResize(PVDSCRIPTARG paScriptArgs, void *pvUser)
2020{
2021 int rc = VINF_SUCCESS;
2022 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2023 const char *pcszDisk = paScriptArgs[0].psz;
2024 uint64_t cbDiskNew = paScriptArgs[1].u64;
2025 PVDDISK pDisk = NULL;
2026
2027 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
2028 if (pDisk)
2029 {
2030 rc = VDResize(pDisk->pVD, cbDiskNew, &pDisk->PhysGeom, &pDisk->LogicalGeom, NULL);
2031 }
2032 else
2033 rc = VERR_NOT_FOUND;
2034
2035 return rc;
2036}
2037
2038static DECLCALLBACK(int) vdScriptHandlerSetFileBackend(PVDSCRIPTARG paScriptArgs, void *pvUser)
2039{
2040 int rc = VINF_SUCCESS;
2041 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2042 const char *pcszBackend = paScriptArgs[0].psz;
2043
2044 RTStrFree(pGlob->pszIoBackend);
2045 pGlob->pszIoBackend = RTStrDup(pcszBackend);
2046 if (!pGlob->pszIoBackend)
2047 rc = VERR_NO_MEMORY;
2048
2049 return rc;
2050}
2051
2052static DECLCALLBACK(int) vdScriptHandlerLoadPlugin(PVDSCRIPTARG paScriptArgs, void *pvUser)
2053{
2054 RT_NOREF(pvUser);
2055 const char *pcszPlugin = paScriptArgs[0].psz;
2056
2057 return VDPluginLoadFromFilename(pcszPlugin);
2058}
2059
2060static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation,
2061 uint32_t fOpen,
2062 PFNVDCOMPLETED pfnCompleted,
2063 void **ppStorage)
2064{
2065 int rc = VINF_SUCCESS;
2066 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2067 bool fFound = false;
2068
2069 /*
2070 * Some backends use ./ for paths, strip it.
2071 * @todo: Implement proper directory support for the
2072 * memory filesystem.
2073 */
2074 if ( strlen(pszLocation) >= 2
2075 && *pszLocation == '.'
2076 && ( pszLocation[1] == '/'
2077 || pszLocation[1] == '\\'))
2078 pszLocation += 2;
2079
2080 /* Check if the file exists. */
2081 PVDFILE pIt;
2082 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
2083 {
2084 if (!RTStrCmp(pIt->pszName, pszLocation))
2085 {
2086 fFound = true;
2087 break;
2088 }
2089 }
2090
2091 if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE)
2092 {
2093 /* If the file exists delete the memory disk. */
2094 if (fFound)
2095 rc = VDIoBackendStorageSetSize(pIt->pIoStorage, 0);
2096 else
2097 {
2098 /* Create completey new. */
2099 pIt = (PVDFILE)RTMemAllocZ(sizeof(VDFILE));
2100 if (pIt)
2101 {
2102 pIt->pszName = RTStrDup(pszLocation);
2103
2104 if (pIt->pszName)
2105 {
2106 rc = VDIoBackendStorageCreate(pGlob->pIoBackend, pGlob->pszIoBackend,
2107 pszLocation, pfnCompleted, &pIt->pIoStorage);
2108 }
2109 else
2110 rc = VERR_NO_MEMORY;
2111
2112 if (RT_FAILURE(rc))
2113 {
2114 if (pIt->pszName)
2115 RTStrFree(pIt->pszName);
2116 RTMemFree(pIt);
2117 }
2118 else
2119 RTListAppend(&pGlob->ListFiles, &pIt->Node);
2120 }
2121 else
2122 rc = VERR_NO_MEMORY;
2123 }
2124 }
2125 else if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN)
2126 {
2127 if (!fFound)
2128 rc = VERR_FILE_NOT_FOUND;
2129 }
2130 else
2131 rc = VERR_INVALID_PARAMETER;
2132
2133 if (RT_SUCCESS(rc))
2134 {
2135 AssertPtr(pIt);
2136 PVDSTORAGE pStorage = (PVDSTORAGE)RTMemAllocZ(sizeof(VDSTORAGE));
2137 if (pStorage)
2138 {
2139 pStorage->pFile = pIt;
2140 pStorage->pfnComplete = pfnCompleted;
2141 *ppStorage = pStorage;
2142 }
2143 else
2144 rc = VERR_NO_MEMORY;
2145 }
2146
2147 return rc;
2148}
2149
2150static DECLCALLBACK(int) tstVDIoFileClose(void *pvUser, void *pStorage)
2151{
2152 RT_NOREF1(pvUser);
2153 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2154
2155 RTMemFree(pIoStorage);
2156 return VINF_SUCCESS;
2157}
2158
2159static DECLCALLBACK(int) tstVDIoFileDelete(void *pvUser, const char *pcszFilename)
2160{
2161 int rc = VINF_SUCCESS;
2162 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2163 bool fFound = false;
2164
2165 /*
2166 * Some backends use ./ for paths, strip it.
2167 * @todo: Implement proper directory support for the
2168 * memory filesystem.
2169 */
2170 if ( strlen(pcszFilename) >= 2
2171 && *pcszFilename == '.'
2172 && pcszFilename[1] == '/')
2173 pcszFilename += 2;
2174
2175 /* Check if the file exists. */
2176 PVDFILE pIt;
2177 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
2178 {
2179 if (!RTStrCmp(pIt->pszName, pcszFilename))
2180 {
2181 fFound = true;
2182 break;
2183 }
2184 }
2185
2186 if (fFound)
2187 {
2188 RTListNodeRemove(&pIt->Node);
2189 VDIoBackendStorageDestroy(pIt->pIoStorage);
2190 RTStrFree(pIt->pszName);
2191 RTMemFree(pIt);
2192 }
2193 else
2194 rc = VERR_FILE_NOT_FOUND;
2195
2196 return rc;
2197}
2198
2199static DECLCALLBACK(int) tstVDIoFileMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
2200{
2201 RT_NOREF1(fMove);
2202 int rc = VINF_SUCCESS;
2203 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2204 bool fFound = false;
2205
2206 /* Check if the file exists. */
2207 PVDFILE pIt;
2208 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
2209 {
2210 if (!RTStrCmp(pIt->pszName, pcszSrc))
2211 {
2212 fFound = true;
2213 break;
2214 }
2215 }
2216
2217 if (fFound)
2218 {
2219 char *pszNew = RTStrDup(pcszDst);
2220 if (pszNew)
2221 {
2222 RTStrFree(pIt->pszName);
2223 pIt->pszName = pszNew;
2224 }
2225 else
2226 rc = VERR_NO_MEMORY;
2227 }
2228 else
2229 rc = VERR_FILE_NOT_FOUND;
2230
2231 return rc;
2232}
2233
2234static DECLCALLBACK(int) tstVDIoFileGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
2235{
2236 RT_NOREF2(pvUser, pcszFilename);
2237 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
2238
2239 *pcbFreeSpace = ~0ULL; /** @todo Implement */
2240 return VINF_SUCCESS;
2241}
2242
2243static DECLCALLBACK(int) tstVDIoFileGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
2244{
2245 RT_NOREF2(pvUser, pcszFilename);
2246 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
2247
2248 /** @todo Implement */
2249 return VINF_SUCCESS;
2250}
2251
2252static DECLCALLBACK(int) tstVDIoFileGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
2253{
2254 RT_NOREF1(pvUser);
2255 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2256
2257 return VDIoBackendStorageGetSize(pIoStorage->pFile->pIoStorage, pcbSize);
2258}
2259
2260static DECLCALLBACK(int) tstVDIoFileSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
2261{
2262 RT_NOREF1(pvUser);
2263 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2264
2265 return VDIoBackendStorageSetSize(pIoStorage->pFile->pIoStorage, cbSize);
2266}
2267
2268static DECLCALLBACK(int) tstVDIoFileSetAllocationSize(void *pvUser, void *pStorage, uint64_t cbSize, uint32_t fFlags)
2269{
2270 RT_NOREF4(pvUser, pStorage, cbSize, fFlags);
2271 return VERR_NOT_SUPPORTED;
2272}
2273
2274static DECLCALLBACK(int) tstVDIoFileWriteSync(void *pvUser, void *pStorage, uint64_t uOffset,
2275 const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten)
2276{
2277 RT_NOREF1(pvUser);
2278 int rc = VINF_SUCCESS;
2279 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2280
2281 RTSGBUF SgBuf;
2282 RTSGSEG Seg;
2283
2284 Seg.pvSeg = (void *)pvBuffer;
2285 Seg.cbSeg = cbBuffer;
2286 RTSgBufInit(&SgBuf, &Seg, 1);
2287 rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_WRITE, uOffset,
2288 cbBuffer, &SgBuf, NULL, true /* fSync */);
2289 if (RT_SUCCESS(rc))
2290 {
2291 pIoStorage->pFile->cWrites++;
2292 if (pcbWritten)
2293 *pcbWritten = cbBuffer;
2294 }
2295
2296 return rc;
2297}
2298
2299static DECLCALLBACK(int) tstVDIoFileReadSync(void *pvUser, void *pStorage, uint64_t uOffset,
2300 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
2301{
2302 RT_NOREF1(pvUser);
2303 int rc = VINF_SUCCESS;
2304 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2305
2306 RTSGBUF SgBuf;
2307 RTSGSEG Seg;
2308
2309 Seg.pvSeg = pvBuffer;
2310 Seg.cbSeg = cbBuffer;
2311 RTSgBufInit(&SgBuf, &Seg, 1);
2312 rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_READ, uOffset,
2313 cbBuffer, &SgBuf, NULL, true /* fSync */);
2314 if (RT_SUCCESS(rc))
2315 {
2316 pIoStorage->pFile->cReads++;
2317 if (pcbRead)
2318 *pcbRead = cbBuffer;
2319 }
2320
2321 return rc;
2322}
2323
2324static DECLCALLBACK(int) tstVDIoFileFlushSync(void *pvUser, void *pStorage)
2325{
2326 RT_NOREF1(pvUser);
2327 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2328 int rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_FLUSH, 0,
2329 0, NULL, NULL, true /* fSync */);
2330 pIoStorage->pFile->cFlushes++;
2331 return rc;
2332}
2333
2334static DECLCALLBACK(int) tstVDIoFileReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
2335 PCRTSGSEG paSegments, size_t cSegments,
2336 size_t cbRead, void *pvCompletion,
2337 void **ppTask)
2338{
2339 RT_NOREF2(pvUser, ppTask);
2340 int rc = VINF_SUCCESS;
2341 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2342 RTSGBUF SgBuf;
2343
2344 RTSgBufInit(&SgBuf, paSegments, cSegments);
2345 rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_READ, uOffset,
2346 cbRead, &SgBuf, pvCompletion, false /* fSync */);
2347 if (RT_SUCCESS(rc))
2348 {
2349 pIoStorage->pFile->cAsyncReads++;
2350 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2351 }
2352
2353 return rc;
2354}
2355
2356static DECLCALLBACK(int) tstVDIoFileWriteAsync(void *pvUser, void *pStorage, uint64_t uOffset,
2357 PCRTSGSEG paSegments, size_t cSegments,
2358 size_t cbWrite, void *pvCompletion,
2359 void **ppTask)
2360{
2361 RT_NOREF2(pvUser, ppTask);
2362 int rc = VINF_SUCCESS;
2363 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2364 RTSGBUF SgBuf;
2365
2366 RTSgBufInit(&SgBuf, paSegments, cSegments);
2367 rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_WRITE, uOffset,
2368 cbWrite, &SgBuf, pvCompletion, false /* fSync */);
2369 if (RT_SUCCESS(rc))
2370 {
2371 pIoStorage->pFile->cAsyncWrites++;
2372 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2373 }
2374
2375 return rc;
2376}
2377
2378static DECLCALLBACK(int) tstVDIoFileFlushAsync(void *pvUser, void *pStorage, void *pvCompletion,
2379 void **ppTask)
2380{
2381 RT_NOREF2(pvUser, ppTask);
2382 int rc = VINF_SUCCESS;
2383 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2384
2385 rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_FLUSH, 0,
2386 0, NULL, pvCompletion, false /* fSync */);
2387 if (RT_SUCCESS(rc))
2388 {
2389 pIoStorage->pFile->cAsyncFlushes++;
2390 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2391 }
2392
2393 return rc;
2394}
2395
2396static int tstVDIoTestInit(PVDIOTEST pIoTest, PVDTESTGLOB pGlob, bool fRandomAcc, uint32_t cSegsMax,
2397 uint64_t cbIo, size_t cbBlkSize, uint64_t offStart, uint64_t offEnd,
2398 unsigned uWriteChance, PVDPATTERN pPattern)
2399{
2400 int rc = VINF_SUCCESS;
2401
2402 RT_ZERO(*pIoTest);
2403 pIoTest->fRandomAccess = fRandomAcc;
2404 pIoTest->cbIo = cbIo;
2405 pIoTest->cbBlkIo = cbBlkSize;
2406 pIoTest->offStart = offStart;
2407 pIoTest->offEnd = offEnd;
2408 pIoTest->uWriteChance = uWriteChance;
2409 pIoTest->cSegsMax = cSegsMax;
2410 pIoTest->pIoRnd = pGlob->pIoRnd;
2411 pIoTest->pPattern = pPattern;
2412
2413 if (fRandomAcc)
2414 {
2415 uint64_t cbRange = pIoTest->offEnd < pIoTest->offStart
2416 ? pIoTest->offStart - pIoTest->offEnd
2417 : pIoTest->offEnd - pIoTest->offStart;
2418
2419 pIoTest->u.Rnd.cBlocks = (uint32_t)(cbRange / cbBlkSize + (cbRange % cbBlkSize ? 1 : 0));
2420 pIoTest->u.Rnd.cBlocksLeft = pIoTest->u.Rnd.cBlocks;
2421 pIoTest->u.Rnd.pbMapAccessed = (uint8_t *)RTMemAllocZ(pIoTest->u.Rnd.cBlocks / 8
2422 + ((pIoTest->u.Rnd.cBlocks % 8)
2423 ? 1
2424 : 0));
2425 if (!pIoTest->u.Rnd.pbMapAccessed)
2426 rc = VERR_NO_MEMORY;
2427 }
2428 else
2429 pIoTest->u.offNext = pIoTest->offEnd < pIoTest->offStart ? pIoTest->offStart - cbBlkSize : offStart;
2430
2431 return rc;
2432}
2433
2434static void tstVDIoTestDestroy(PVDIOTEST pIoTest)
2435{
2436 if (pIoTest->fRandomAccess)
2437 RTMemFree(pIoTest->u.Rnd.pbMapAccessed);
2438}
2439
2440static bool tstVDIoTestRunning(PVDIOTEST pIoTest)
2441{
2442 return pIoTest->cbIo > 0;
2443}
2444
2445static bool tstVDIoTestReqOutstanding(PTSTVDIOREQ pIoReq)
2446{
2447 return pIoReq->fOutstanding;
2448}
2449
2450static uint32_t tstVDIoTestReqInitSegments(PVDIOTEST pIoTest, PRTSGSEG paSegs, uint32_t cSegs, void *pvBuf, size_t cbBuf)
2451{
2452 uint8_t *pbBuf = (uint8_t *)pvBuf;
2453 size_t cSectorsLeft = cbBuf / 512;
2454 uint32_t iSeg = 0;
2455
2456 /* Init all but the last segment which needs to take the rest. */
2457 while ( iSeg < cSegs - 1
2458 && cSectorsLeft)
2459 {
2460 uint32_t cThisSectors = VDIoRndGetU32Ex(pIoTest->pIoRnd, 1, (uint32_t)cSectorsLeft / 2);
2461 size_t cbThisBuf = cThisSectors * 512;
2462
2463 paSegs[iSeg].pvSeg = pbBuf;
2464 paSegs[iSeg].cbSeg = cbThisBuf;
2465 pbBuf += cbThisBuf;
2466 cSectorsLeft -= cThisSectors;
2467 iSeg++;
2468 }
2469
2470 if (cSectorsLeft)
2471 {
2472 paSegs[iSeg].pvSeg = pbBuf;
2473 paSegs[iSeg].cbSeg = cSectorsLeft * 512;
2474 iSeg++;
2475 }
2476
2477 return iSeg;
2478}
2479
2480/**
2481 * Returns true with the given chance in percent.
2482 *
2483 * @returns true or false
2484 * @param iPercentage The percentage of the chance to return true.
2485 */
2486static bool tstVDIoTestIsTrue(PVDIOTEST pIoTest, int iPercentage)
2487{
2488 int uRnd = VDIoRndGetU32Ex(pIoTest->pIoRnd, 0, 100);
2489
2490 return (uRnd < iPercentage); /* This should be enough for our purpose */
2491}
2492
2493static int tstVDIoTestReqInit(PVDIOTEST pIoTest, PTSTVDIOREQ pIoReq, void *pvUser)
2494{
2495 int rc = VINF_SUCCESS;
2496
2497 if (pIoTest->cbIo)
2498 {
2499 /* Read or Write? */
2500 pIoReq->enmTxDir = tstVDIoTestIsTrue(pIoTest, pIoTest->uWriteChance) ? TSTVDIOREQTXDIR_WRITE : TSTVDIOREQTXDIR_READ;
2501 pIoReq->cbReq = RT_MIN(pIoTest->cbBlkIo, pIoTest->cbIo);
2502 pIoTest->cbIo -= pIoReq->cbReq;
2503
2504 void *pvBuf = NULL;
2505
2506 if (pIoReq->enmTxDir == TSTVDIOREQTXDIR_WRITE)
2507 {
2508 if (pIoTest->pPattern)
2509 rc = tstVDIoPatternGetBuffer(pIoTest->pPattern, &pvBuf, pIoReq->cbReq);
2510 else
2511 rc = VDIoRndGetBuffer(pIoTest->pIoRnd, &pvBuf, pIoReq->cbReq);
2512 AssertRC(rc);
2513 }
2514 else
2515 {
2516 /* Read */
2517 pvBuf = pIoReq->pvBufRead;
2518 }
2519
2520 if (RT_SUCCESS(rc))
2521 {
2522 pIoReq->pvBuf = pvBuf;
2523 uint32_t cSegsMax = VDIoRndGetU32Ex(pIoTest->pIoRnd, 1, RT_MIN(pIoTest->cSegsMax, RT_ELEMENTS(pIoReq->aSegs)));
2524 pIoReq->cSegs = tstVDIoTestReqInitSegments(pIoTest, &pIoReq->aSegs[0], cSegsMax, pvBuf, pIoReq->cbReq);
2525 RTSgBufInit(&pIoReq->SgBuf, &pIoReq->aSegs[0], pIoReq->cSegs);
2526
2527 if (pIoTest->fRandomAccess)
2528 {
2529 int idx = -1;
2530
2531 idx = ASMBitFirstClear(pIoTest->u.Rnd.pbMapAccessed, pIoTest->u.Rnd.cBlocks);
2532
2533 /* In case this is the last request we don't need to search further. */
2534 if (pIoTest->u.Rnd.cBlocksLeft > 1)
2535 {
2536 int idxIo;
2537 idxIo = VDIoRndGetU32Ex(pIoTest->pIoRnd, idx, pIoTest->u.Rnd.cBlocks - 1);
2538
2539 /*
2540 * If the bit is marked free use it, otherwise search for the next free bit
2541 * and if that doesn't work use the first free bit.
2542 */
2543 if (ASMBitTest(pIoTest->u.Rnd.pbMapAccessed, idxIo))
2544 {
2545 idxIo = ASMBitNextClear(pIoTest->u.Rnd.pbMapAccessed, pIoTest->u.Rnd.cBlocks, idxIo);
2546 if (idxIo != -1)
2547 idx = idxIo;
2548 }
2549 else
2550 idx = idxIo;
2551 }
2552
2553 Assert(idx != -1);
2554 pIoReq->off = (uint64_t)idx * pIoTest->cbBlkIo;
2555 pIoTest->u.Rnd.cBlocksLeft--;
2556 if (!pIoTest->u.Rnd.cBlocksLeft)
2557 {
2558 /* New round, clear everything. */
2559 ASMBitClearRange(pIoTest->u.Rnd.pbMapAccessed, 0, pIoTest->u.Rnd.cBlocks);
2560 pIoTest->u.Rnd.cBlocksLeft = pIoTest->u.Rnd.cBlocks;
2561 }
2562 else
2563 ASMBitSet(pIoTest->u.Rnd.pbMapAccessed, idx);
2564 }
2565 else
2566 {
2567 pIoReq->off = pIoTest->u.offNext;
2568 if (pIoTest->offEnd < pIoTest->offStart)
2569 {
2570 pIoTest->u.offNext = pIoTest->u.offNext == 0
2571 ? pIoTest->offEnd - pIoTest->cbBlkIo
2572 : RT_MAX(pIoTest->offEnd, pIoTest->u.offNext - pIoTest->cbBlkIo);
2573 }
2574 else
2575 {
2576 pIoTest->u.offNext = pIoTest->u.offNext + pIoTest->cbBlkIo >= pIoTest->offEnd
2577 ? 0
2578 : RT_MIN(pIoTest->offEnd, pIoTest->u.offNext + pIoTest->cbBlkIo);
2579 }
2580 }
2581 pIoReq->pvUser = pvUser;
2582 pIoReq->fOutstanding = true;
2583 }
2584 }
2585 else
2586 rc = VERR_ACCESS_DENIED;
2587
2588 return rc;
2589}
2590
2591static DECLCALLBACK(void) tstVDIoTestReqComplete(void *pvUser1, void *pvUser2, int rcReq)
2592{
2593 RT_NOREF1(rcReq);
2594 PTSTVDIOREQ pIoReq = (PTSTVDIOREQ)pvUser1;
2595 RTSEMEVENT hEventSem = (RTSEMEVENT)pvUser2;
2596 PVDDISK pDisk = (PVDDISK)pIoReq->pvUser;
2597
2598 LogFlow(("Request %d completed\n", pIoReq->idx));
2599
2600 if (pDisk->pMemDiskVerify)
2601 {
2602 switch (pIoReq->enmTxDir)
2603 {
2604 case TSTVDIOREQTXDIR_READ:
2605 {
2606 RTCritSectEnter(&pDisk->CritSectVerify);
2607
2608 RTSGBUF SgBufCmp;
2609 RTSGSEG SegCmp;
2610 SegCmp.pvSeg = pIoReq->pvBuf;
2611 SegCmp.cbSeg = pIoReq->cbReq;
2612 RTSgBufInit(&SgBufCmp, &SegCmp, 1);
2613
2614 if (VDMemDiskCmp(pDisk->pMemDiskVerify, pIoReq->off, pIoReq->cbReq,
2615 &SgBufCmp))
2616 RTTestFailed(pDisk->pTestGlob->hTest, "Corrupted disk at offset %llu!\n", pIoReq->off);
2617 RTCritSectLeave(&pDisk->CritSectVerify);
2618 break;
2619 }
2620 case TSTVDIOREQTXDIR_WRITE:
2621 {
2622 RTCritSectEnter(&pDisk->CritSectVerify);
2623
2624 RTSGBUF SgBuf;
2625 RTSGSEG Seg;
2626 Seg.pvSeg = pIoReq->pvBuf;
2627 Seg.cbSeg = pIoReq->cbReq;
2628 RTSgBufInit(&SgBuf, &Seg, 1);
2629
2630 int rc = VDMemDiskWrite(pDisk->pMemDiskVerify, pIoReq->off, pIoReq->cbReq,
2631 &SgBuf);
2632 AssertRC(rc);
2633 RTCritSectLeave(&pDisk->CritSectVerify);
2634 break;
2635 }
2636 case TSTVDIOREQTXDIR_FLUSH:
2637 case TSTVDIOREQTXDIR_DISCARD:
2638 break;
2639 }
2640 }
2641
2642 ASMAtomicXchgBool(&pIoReq->fOutstanding, false);
2643 RTSemEventSignal(hEventSem);
2644 return;
2645}
2646
2647/**
2648 * Returns the disk handle by name or NULL if not found
2649 *
2650 * @returns Disk handle or NULL if the disk could not be found.
2651 *
2652 * @param pGlob Global test state.
2653 * @param pcszDisk Name of the disk to get.
2654 */
2655static PVDDISK tstVDIoGetDiskByName(PVDTESTGLOB pGlob, const char *pcszDisk)
2656{
2657 bool fFound = false;
2658
2659 LogFlowFunc(("pGlob=%#p pcszDisk=%s\n", pGlob, pcszDisk));
2660
2661 PVDDISK pIt;
2662 RTListForEach(&pGlob->ListDisks, pIt, VDDISK, ListNode)
2663 {
2664 if (!RTStrCmp(pIt->pszName, pcszDisk))
2665 {
2666 fFound = true;
2667 break;
2668 }
2669 }
2670
2671 LogFlowFunc(("return %#p\n", fFound ? pIt : NULL));
2672 return fFound ? pIt : NULL;
2673}
2674
2675/**
2676 * Returns the I/O pattern handle by name of NULL if not found.
2677 *
2678 * @returns I/O pattern handle or NULL if the pattern could not be found.
2679 *
2680 * @param pGlob Global test state.
2681 * @param pcszName Name of the pattern.
2682 */
2683static PVDPATTERN tstVDIoGetPatternByName(PVDTESTGLOB pGlob, const char *pcszName)
2684{
2685 bool fFound = false;
2686
2687 LogFlowFunc(("pGlob=%#p pcszName=%s\n", pGlob, pcszName));
2688
2689 PVDPATTERN pIt;
2690 RTListForEach(&pGlob->ListPatterns, pIt, VDPATTERN, ListNode)
2691 {
2692 if (!RTStrCmp(pIt->pszName, pcszName))
2693 {
2694 fFound = true;
2695 break;
2696 }
2697 }
2698
2699 LogFlowFunc(("return %#p\n", fFound ? pIt : NULL));
2700 return fFound ? pIt : NULL;
2701}
2702
2703/**
2704 * Creates a new pattern with the given name and an
2705 * allocated pattern buffer.
2706 *
2707 * @returns Pointer to a new pattern buffer or NULL on failure.
2708 * @param pcszName Name of the pattern.
2709 * @param cbPattern Size of the pattern buffer.
2710 */
2711static PVDPATTERN tstVDIoPatternCreate(const char *pcszName, size_t cbPattern)
2712{
2713 PVDPATTERN pPattern = (PVDPATTERN)RTMemAllocZ(sizeof(VDPATTERN));
2714 char *pszName = RTStrDup(pcszName);
2715 void *pvPattern = RTMemAllocZ(cbPattern);
2716
2717 if (pPattern && pszName && pvPattern)
2718 {
2719 pPattern->pszName = pszName;
2720 pPattern->pvPattern = pvPattern;
2721 pPattern->cbPattern = cbPattern;
2722 }
2723 else
2724 {
2725 if (pPattern)
2726 RTMemFree(pPattern);
2727 if (pszName)
2728 RTStrFree(pszName);
2729 if (pvPattern)
2730 RTMemFree(pvPattern);
2731
2732 pPattern = NULL;
2733 pszName = NULL;
2734 pvPattern = NULL;
2735 }
2736
2737 return pPattern;
2738}
2739
2740static int tstVDIoPatternGetBuffer(PVDPATTERN pPattern, void **ppv, size_t cb)
2741{
2742 AssertPtrReturn(pPattern, VERR_INVALID_POINTER);
2743 AssertPtrReturn(ppv, VERR_INVALID_POINTER);
2744 AssertReturn(cb > 0, VERR_INVALID_PARAMETER);
2745
2746 if (cb > pPattern->cbPattern)
2747 return VERR_INVALID_PARAMETER;
2748
2749 *ppv = pPattern->pvPattern;
2750 return VINF_SUCCESS;
2751}
2752
2753/**
2754 * Executes the given script.
2755 *
2756 * @returns nothing.
2757 * @param pszName The script name.
2758 * @param pszScript The script to execute.
2759 */
2760static void tstVDIoScriptExec(const char *pszName, const char *pszScript)
2761{
2762 int rc = VINF_SUCCESS;
2763 VDTESTGLOB GlobTest; /**< Global test data. */
2764
2765 memset(&GlobTest, 0, sizeof(VDTESTGLOB));
2766 RTListInit(&GlobTest.ListFiles);
2767 RTListInit(&GlobTest.ListDisks);
2768 RTListInit(&GlobTest.ListPatterns);
2769 GlobTest.pszIoBackend = RTStrDup("memory");
2770 if (!GlobTest.pszIoBackend)
2771 {
2772 RTPrintf("Out of memory allocating I/O backend string\n");
2773 return;
2774 }
2775
2776 /* Init global test data. */
2777 GlobTest.VDIfError.pfnError = tstVDError;
2778 GlobTest.VDIfError.pfnMessage = tstVDMessage;
2779
2780 rc = VDInterfaceAdd(&GlobTest.VDIfError.Core, "tstVDIo_VDIError", VDINTERFACETYPE_ERROR,
2781 NULL, sizeof(VDINTERFACEERROR), &GlobTest.pInterfacesDisk);
2782 AssertRC(rc);
2783
2784 GlobTest.VDIfIo.pfnOpen = tstVDIoFileOpen;
2785 GlobTest.VDIfIo.pfnClose = tstVDIoFileClose;
2786 GlobTest.VDIfIo.pfnDelete = tstVDIoFileDelete;
2787 GlobTest.VDIfIo.pfnMove = tstVDIoFileMove;
2788 GlobTest.VDIfIo.pfnGetFreeSpace = tstVDIoFileGetFreeSpace;
2789 GlobTest.VDIfIo.pfnGetModificationTime = tstVDIoFileGetModificationTime;
2790 GlobTest.VDIfIo.pfnGetSize = tstVDIoFileGetSize;
2791 GlobTest.VDIfIo.pfnSetSize = tstVDIoFileSetSize;
2792 GlobTest.VDIfIo.pfnSetAllocationSize = tstVDIoFileSetAllocationSize;
2793 GlobTest.VDIfIo.pfnWriteSync = tstVDIoFileWriteSync;
2794 GlobTest.VDIfIo.pfnReadSync = tstVDIoFileReadSync;
2795 GlobTest.VDIfIo.pfnFlushSync = tstVDIoFileFlushSync;
2796 GlobTest.VDIfIo.pfnReadAsync = tstVDIoFileReadAsync;
2797 GlobTest.VDIfIo.pfnWriteAsync = tstVDIoFileWriteAsync;
2798 GlobTest.VDIfIo.pfnFlushAsync = tstVDIoFileFlushAsync;
2799
2800 rc = VDInterfaceAdd(&GlobTest.VDIfIo.Core, "tstVDIo_VDIIo", VDINTERFACETYPE_IO,
2801 &GlobTest, sizeof(VDINTERFACEIO), &GlobTest.pInterfacesImages);
2802 AssertRC(rc);
2803
2804 rc = RTTestCreate(pszName, &GlobTest.hTest);
2805 if (RT_SUCCESS(rc))
2806 {
2807 /* Init I/O backend. */
2808 rc = VDIoBackendCreate(&GlobTest.pIoBackend);
2809 if (RT_SUCCESS(rc))
2810 {
2811 VDSCRIPTCTX hScriptCtx = NULL;
2812 rc = VDScriptCtxCreate(&hScriptCtx);
2813 if (RT_SUCCESS(rc))
2814 {
2815 RTTEST_CHECK_RC_OK(GlobTest.hTest,
2816 VDScriptCtxCallbacksRegister(hScriptCtx, g_aScriptActions, g_cScriptActions, &GlobTest));
2817
2818 RTTestBanner(GlobTest.hTest);
2819 rc = VDScriptCtxLoadScript(hScriptCtx, pszScript);
2820 if (RT_FAILURE(rc))
2821 {
2822 RTPrintf("Loading the script failed rc=%Rrc\n", rc);
2823 }
2824 else
2825 rc = VDScriptCtxCallFn(hScriptCtx, "main", NULL, 0);
2826 VDScriptCtxDestroy(hScriptCtx);
2827 }
2828
2829 /* Clean up all leftover resources. */
2830 PVDPATTERN pPatternIt, pPatternItNext;
2831 RTListForEachSafe(&GlobTest.ListPatterns, pPatternIt, pPatternItNext, VDPATTERN, ListNode)
2832 {
2833 RTPrintf("Cleanup: Leftover pattern \"%s\", deleting...\n", pPatternIt->pszName);
2834 RTListNodeRemove(&pPatternIt->ListNode);
2835 RTMemFree(pPatternIt->pvPattern);
2836 RTStrFree(pPatternIt->pszName);
2837 RTMemFree(pPatternIt);
2838 }
2839
2840 PVDDISK pDiskIt, pDiskItNext;
2841 RTListForEachSafe(&GlobTest.ListDisks, pDiskIt, pDiskItNext, VDDISK, ListNode)
2842 {
2843 RTPrintf("Cleanup: Leftover disk \"%s\", deleting...\n", pDiskIt->pszName);
2844 RTListNodeRemove(&pDiskIt->ListNode);
2845 VDDestroy(pDiskIt->pVD);
2846 if (pDiskIt->pMemDiskVerify)
2847 {
2848 VDMemDiskDestroy(pDiskIt->pMemDiskVerify);
2849 RTCritSectDelete(&pDiskIt->CritSectVerify);
2850 }
2851 RTStrFree(pDiskIt->pszName);
2852 RTMemFree(pDiskIt);
2853 }
2854
2855 PVDFILE pFileIt, pFileItNext;
2856 RTListForEachSafe(&GlobTest.ListFiles, pFileIt, pFileItNext, VDFILE, Node)
2857 {
2858 RTPrintf("Cleanup: Leftover file \"%s\", deleting...\n", pFileIt->pszName);
2859 RTListNodeRemove(&pFileIt->Node);
2860 VDIoBackendStorageDestroy(pFileIt->pIoStorage);
2861 RTStrFree(pFileIt->pszName);
2862 RTMemFree(pFileIt);
2863 }
2864
2865 VDIoBackendDestroy(GlobTest.pIoBackend);
2866 }
2867 else
2868 RTPrintf("Creating the I/O backend failed rc=%Rrc\n", rc);
2869
2870 RTTestSummaryAndDestroy(GlobTest.hTest);
2871 }
2872 else
2873 RTStrmPrintf(g_pStdErr, "tstVDIo: fatal error: RTTestCreate failed with rc=%Rrc\n", rc);
2874
2875 RTStrFree(GlobTest.pszIoBackend);
2876}
2877
2878/**
2879 * Executes the given I/O script using the new scripting engine.
2880 *
2881 * @returns nothing.
2882 *
2883 * @param pcszFilename The script to execute.
2884 */
2885static void tstVDIoScriptRun(const char *pcszFilename)
2886{
2887 int rc = VINF_SUCCESS;
2888 void *pvFile = NULL;
2889 size_t cbFile = 0;
2890
2891 rc = RTFileReadAll(pcszFilename, &pvFile, &cbFile);
2892 if (RT_SUCCESS(rc))
2893 {
2894 char *pszScript = RTStrDupN((char *)pvFile, cbFile);
2895 RTFileReadAllFree(pvFile, cbFile);
2896
2897 AssertPtr(pszScript);
2898 tstVDIoScriptExec(pcszFilename, pszScript);
2899 RTStrFree(pszScript);
2900 }
2901 else
2902 RTPrintf("Opening the script failed: %Rrc\n", rc);
2903
2904}
2905
2906/**
2907 * Run builtin tests.
2908 *
2909 * @returns nothing.
2910 */
2911static void tstVDIoRunBuiltinTests(void)
2912{
2913 /* 32bit hosts are excluded because of the 4GB address space. */
2914#if HC_ARCH_BITS == 32
2915 RTStrmPrintf(g_pStdErr, "tstVDIo: Running on a 32bit host is not supported for the builtin tests, skipping\n");
2916 return;
2917#else
2918 /*
2919 * We need quite a bit of RAM for the builtin tests. Skip it if there
2920 * is not enough free RAM available.
2921 */
2922 uint64_t cbFree = 0;
2923 int rc = RTSystemQueryAvailableRam(&cbFree);
2924 if ( RT_FAILURE(rc)
2925 || cbFree < (UINT64_C(6) * _1G))
2926 {
2927 RTStrmPrintf(g_pStdErr, "tstVDIo: fatal error: Failed to query available RAM or not enough available, skipping (rc=%Rrc cbFree=%llu)\n",
2928 rc, cbFree);
2929 return;
2930 }
2931
2932 for (unsigned i = 0; i < g_cVDIoTests; i++)
2933 {
2934 char *pszScript = RTStrDupN((const char *)g_aVDIoTests[i].pch, g_aVDIoTests[i].cb);
2935
2936 AssertPtr(pszScript);
2937 tstVDIoScriptExec(g_aVDIoTests[i].pszName, pszScript);
2938 RTStrFree(pszScript);
2939 }
2940#endif
2941}
2942
2943/**
2944 * Shows help message.
2945 */
2946static void printUsage(void)
2947{
2948 RTPrintf("Usage:\n"
2949 "--script <filename> Script to execute\n");
2950}
2951
2952static const RTGETOPTDEF g_aOptions[] =
2953{
2954 { "--script", 's', RTGETOPT_REQ_STRING },
2955 { "--help", 'h', RTGETOPT_REQ_NOTHING }
2956};
2957
2958int main(int argc, char *argv[])
2959{
2960 RTR3InitExe(argc, &argv, 0);
2961 int rc;
2962 RTGETOPTUNION ValueUnion;
2963 RTGETOPTSTATE GetState;
2964 char c;
2965
2966 rc = VDInit();
2967 if (RT_FAILURE(rc))
2968 return RTEXITCODE_FAILURE;
2969
2970 if (argc == 1)
2971 {
2972 tstVDIoRunBuiltinTests();
2973 return RTEXITCODE_SUCCESS;
2974 }
2975
2976 RTGetOptInit(&GetState, argc, argv, g_aOptions,
2977 RT_ELEMENTS(g_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
2978
2979 while ( RT_SUCCESS(rc)
2980 && (c = RTGetOpt(&GetState, &ValueUnion)))
2981 {
2982 switch (c)
2983 {
2984 case 's':
2985 tstVDIoScriptRun(ValueUnion.psz);
2986 break;
2987 case 'h':
2988 printUsage();
2989 break;
2990 default: /* Default is to run built in tests if no arguments are given (automated testing). */
2991 tstVDIoRunBuiltinTests();
2992 }
2993 }
2994
2995 rc = VDShutdown();
2996 if (RT_FAILURE(rc))
2997 RTPrintf("tstVDIo: unloading backends failed! rc=%Rrc\n", rc);
2998
2999 return RTEXITCODE_SUCCESS;
3000}
3001
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