VirtualBox

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

Last change on this file since 45733 was 44942, checked in by vboxsync, 12 years ago

Storage/tstVDIo: Switch to VDScript interpreter

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