VirtualBox

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

Last change on this file since 55180 was 55083, checked in by vboxsync, 10 years ago

tstVDIo: Hopefully fix errors on testboxes regarding to deep sub test nesting

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