VirtualBox

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

Last change on this file since 46876 was 46247, checked in by vboxsync, 12 years ago

Storage/tstVDIo: Integrate runtime async I/O manager and add a file based storage backend in addition to the memory based backend

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