VirtualBox

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

Last change on this file since 65381 was 64766, checked in by vboxsync, 8 years ago

src/VBox: Make the use of the iterator for RTListForEach()/RTListForEachSafe() more obvious. There is no need to initialize the iterator and we also must not depend on the iterator being NULL if the list was empty.

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