VirtualBox

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

Last change on this file since 52111 was 52111, checked in by vboxsync, 11 years ago

Storage/testcases: Include the tests scripts into tstVDIo for automated testing

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette